StandardGame: Problems with Preferences and GameSettings

Good evening everyone,

I tried migrating a small app to using StandardGame and am still experiancing problems:

The GameSettingsPanel will contain no options for depth and frequency

Although options are defined in the GameSettingsPanel they seem to be overwritten and remain empty when the dialog popps up.

This happens when executing the code in setMenuOptions(int width, int height) in lines 220 and 221 in

   public void setMenuOptions(int width, int height) {
      Vector<DisplayMode> availableModes = getAvailableModesRes(allModes,
            width, height);
      HashSet<String> depths = new HashSet<String>();
      HashSet<String> frequencies = new HashSet<String>();

      for (DisplayMode aMode : availableModes) {
      for (String oneDepth : depths) {
      for (String oneFreq : frequencies) {

The game will not start when selecting 1600x1200 resolution
My native resolution is 1920x1200. The first issue is that this one is not available on the panel and it lacks configuration possibilities. Starting the game with 1600x1200 will result in a NumberFormatException when parsing the depth (which seems to be null due to the first problem.

Exception in thread "AWT-EventQueue-0" java.lang.NumberFormatException: null
   at java.lang.Integer.parseInt(
   at java.lang.Integer.parseInt(
   at com.jmex.editors.swing.settings.GameSettingsPanel.apply(
   at com.jmex.editors.swing.settings.GameSettingsPanel$2.actionPerformed(
   at javax.swing.AbstractButton.fireActionPerformed(
   at javax.swing.AbstractButton$Handler.actionPerformed(
   at javax.swing.DefaultButtonModel.fireActionPerformed(
   at javax.swing.DefaultButtonModel.setPressed(
   at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(
   at java.awt.Component.processMouseEvent(
   at javax.swing.JComponent.processMouseEvent(
   at java.awt.Component.processEvent(
   at java.awt.Container.processEvent(
   at java.awt.Component.dispatchEventImpl(
   at java.awt.Container.dispatchEventImpl(
   at java.awt.Component.dispatchEvent(
   at java.awt.LightweightDispatcher.retargetMouseEvent(
   at java.awt.LightweightDispatcher.processMouseEvent(
   at java.awt.LightweightDispatcher.dispatchEvent(
   at java.awt.Container.dispatchEventImpl(
   at java.awt.Window.dispatchEventImpl(
   at java.awt.Component.dispatchEvent(
   at java.awt.EventQueue.dispatchEvent(
   at java.awt.EventDispatchThread.pumpOneEventForFilters(
   at java.awt.EventDispatchThread.pumpEventsForFilter(
   at java.awt.EventDispatchThread.pumpEventsForHierarchy(
   at java.awt.EventDispatchThread.pumpEvents(
   at java.awt.EventDispatchThread.pumpEvents(

Interesting though that all other resolutions will get the game started.

Minor issue: GameSettingsPanel behaves strangely
The X in the GameSettingsPanel is not working. Clicking "Cancel" will start the game anyway.

Any ideas / suggestions / questions / confirmations?

Thank you.

I can confirm that the cancel-button issue has been fixed long ago since I filed the bug. You might be using a very old jME version.

That's interesting. I'm using the version from the repository. Am I doing something wrong?

…I remember applying the fix myself. :o

It would seem detecting depth and frequency for 1600x1200 for your machine is not working properly or isn't supported.  If you select another video mode it does populate the depth and freq?

Ah, yes, you're right. It does populate those fields.

I now coded the option for my native resolution (1920x1200) into the GameSettingsPanel and it works!

So it appears to me that the following needs to be done:

  • Apply a function to the X-Button in GameSettingsPanel
  • Check the functionality of the Cancel-Button (or else explain to me how I got to have an old version from the repository :))
  • Allow to add or remove options (e.g. resolutions) to the GameSettingsPanel (because it is created by StandardGame which is not intended to be extended).
  • Do not display modes that do not work (e.g. causing depth or freq to be null)

So, just for clarification here…can you change your desktop resolution to 1600x1200?  If so, what refresh rates and color depths are available on your OS?  I'm trying to clarify exactly what is causing the issue.

Explain further what you want by add/remove options.  It auto-detects what your video card is capable of and it seems in your case on 1600x1200 it is not finding anything…if that's resolved then you shouldn't ever need to add/remove from it, right?

We do need to open up to "standard resolutions" if you selected windowed instead of fullscreen though.

I checked and am not allowed to select 1600x1200 for resolution in my screen settings. I am though, allowed to select e.g. 1600x1024.

My screen has a 16:10 format. That may be why, but 1600x1024 ist not exactly 16:10 anyway.

I am using a notebook tft. Those things have a native resolution. They render other resolutions by recalculating the pixels onto the native resolutions (which makes settings other than the native resolution appear somewhat blurry). My native resolution is 1920x1200 (which is 16:10).

If it is of any meaning: I'm runnig a Ubuntu linux and use the nvidia drivers for my GeForce 7900 Go GS.

What I did is to hardcode 1920x1600 into GameSettingsPanel, so I changed (, line 64f.)

   public static final int[][] RESOLUTIONS = { { 640, 480 }, { 800, 600 }, { 1024, 768 }, { 1280, 1024 },
         { 1440, 900 }, { 1600, 1200 } };


   public static final int[][] RESOLUTIONS = { { 640, 480 }, { 800, 600 }, { 1024, 768 }, { 1280, 1024 },
         { 1440, 900 }, { 1600, 1200 }, { 1920, 1200 } };

After that the option 1920x1200 appears in the promt, I can select it and run the game without any problems.

About the auto detection:
You seem to be right that it autodetects what depths and frequencies my video card is capable of, given one of the hardcoded resolutions, but
- it lets me select non suitable video modes (resulting in a NumberFormatException)
- it does not display all the resolution my video card is capable of (that particularly includes my native resolution and it seems to me all other resolutions other that the ones in RESOLUTIONS)

I did a little work on this, but really it needs some more time to really work properly.  It should display the extra screen resolutions you mentioned (let me know if there are others that you want added) and now if you try to continue without selecting a valid depth or frequency it will show an error message and not let you continue.  Hope this at least suits for now and hopefully in the near future I or someone else will take the time to modify this to work much better.

Thank you for the quick fix.

Nevertheless I am some kind of unsatisfied with the solution.

I clicked around in the APIs and found the following possibility to get all available resolutions (DisplayModes):

import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;


      DisplayMode[] modes = null;
      try {
         modes = Display.getAvailableDisplayModes();
      } catch (LWJGLException e) {

Unfortunately the modes contain "dublicates" concerning resolution, having different frequency values.
But that should definitely be used to determine resolutions instead of hardcoding them into the GameSettingsPanel class. That would prevent impossible resolutions from being displayed and would also cover all possibilities regardless which hardware configuration people have.

Also the game still starts when I click cancel. That should really be fixed. It's actually quite annoying if I want to test the GameSettingsPanel and have to start and end the game everytime. The X-Button also still does nothing. It should also cancel the operation.

Thanks a lot and I hope someone fixes the points mentioned above.

Oh, I nearly forgot to ask another little question about the StandardGame settings:

StandardGame limits the framerate (default to 60). The settings have a setter called setFramerate(int framerate); Setting it to another value unfortunately has no effect on the default limit to 60. Am I missing something?

Like I said, it still need a lot of work, but I was trying to resolve your immediate problems.

So, in your code you're calling GameSettingsPanel.prompt(…) and it's returning true even if you hit cancel?  It sure doesn't do that for me and I can't imagine how it could.

not!an!exit said:

Oh, I nearly forgot to ask another little question about the StandardGame settings:
StandardGame limits the framerate (default to 60). The settings have a setter called setFramerate(int framerate); Setting it to another value unfortunately has no effect on the default limit to 60. Am I missing something?

If you have Vertical Sync set to true setFramerate doesn't do anything.  In order to be able to configure the framerate it must be set to false.

Ah, okay, now I get it concerning the Cancel-Button.

Did you mean to set VSync to true? It is preset to false.

If I set it to true and set the framerate to 500, the framerate will go up to about 140 - 150 frames. I had about 600 - 700 fps using SimpleGame. Is that normal?

No, not necessary. I'd actually rather see you building in a proper resolution detection as mentioned above.

Thanks for the explanation. I'm still not quite sure how exactly to design a game multithreaded, but I'll see…

I'll get to that sometime soon.  When I actually first wrote GameSettingsPanel I actually intended it as an example and for development use…it's extended well beyond that now so I suppose supporting it is inevitable. :wink:

package com.gibbon.mfkarpg;

import com.jme.system.GameSettings;
import java.awt.DisplayMode;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListModel;

public class Config extends javax.swing.JFrame {
    private DefaultListModel model;
    private DefaultComboBoxModel resModel = new DefaultComboBoxModel();
    private DefaultComboBoxModel bppModel = new DefaultComboBoxModel();
    private DefaultComboBoxModel freqModel = new DefaultComboBoxModel();
    private GameSettings settings;
    private Object activeLock = new Object();
    private DisplayModeWrapper[] modes;
    private boolean disableUpdate = true;
    public Object getActiveLock(){
        return activeLock;
    private static class DisplayModeWrapper {
        private DisplayMode dm;
        public DisplayModeWrapper(DisplayMode dm){; }
        public String toString(){
            return dm.getWidth()+"x"+dm.getHeight()+"x"+dm.getBitDepth()+" @ "+dm.getRefreshRate()+" Hz";
    public boolean filter(DisplayMode mode){
        int w = 0,h = 0,bpp = 0,freq = 0;
        if (!cmbRes.getSelectedItem().equals("All")){
            String[] res = ((String) cmbRes.getSelectedItem()).split("x");
            w = Integer.valueOf(res[0]);
            h = Integer.valueOf(res[1]);
        if (!cmbBPP.getSelectedItem().equals("All")){
            bpp = Integer.valueOf((String)cmbBPP.getSelectedItem());
        if (!cmbFreq.getSelectedItem().equals("All")){
            freq = Integer.valueOf((String)cmbFreq.getSelectedItem());
        return  (mode.getWidth()==w || w==0)
             && (mode.getHeight()==h || h==0)
             && (mode.getBitDepth()==bpp || bpp==0)
             && (mode.getRefreshRate()==freq || freq==0);
    public void updateModeList(){
        if (disableUpdate) return;
        for (DisplayModeWrapper mode: modes){
            DisplayMode m =;
            if (filter(m)){
    public Config(GameSettings settings) {
        model = new DefaultListModel();
        GraphicsDevice gd = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
        DisplayMode[] m = gd.getDisplayModes();
        modes = new DisplayModeWrapper[m.length];
        for (int i = 0; i < m.length; i++){
            modes[i] = new DisplayModeWrapper(m[i]);
            String res = m[i].getWidth()+"x"+m[i].getHeight();
            if (resModel.getIndexOf(res)==-1){
            String bpp = Integer.toString(m[i].getBitDepth());
            if (bppModel.getIndexOf(bpp)==-1){
            String freq = Integer.toString(m[i].getRefreshRate());
            if (freqModel.getIndexOf(freq)==-1){
        disableUpdate = false;
        int w = settings.getWidth(), h = settings.getHeight(), bpp = settings.getDepth(), freq = settings.getFrequency();
        for (int i = 0; i < modes.length; i++){
            DisplayMode mode = modes[i].dm;
            if (mode.getWidth()==w && mode.getHeight()==h && mode.getBitDepth()==bpp && mode.getRefreshRate()==freq){
    public void waitFor() throws InterruptedException{
        synchronized (activeLock){
            while (isVisible()){
    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
    // <editor-fold defaultstate="collapsed" desc=" Generated Code ">                         
    private void initComponents() {
        lblMode = new javax.swing.JLabel();
        sclMode = new javax.swing.JScrollPane();
        lstMode = new javax.swing.JList();
        btnStart = new javax.swing.JButton();
        btnCancel = new javax.swing.JButton();
        cmbQuality = new javax.swing.JComboBox();
        lblQuality = new javax.swing.JLabel();
        sep = new javax.swing.JSeparator();
        chkFS = new javax.swing.JCheckBox();
        jPanel1 = new javax.swing.JPanel();
        cmbFreq = new javax.swing.JComboBox();
        lblFreq = new javax.swing.JLabel();
        lblBPP = new javax.swing.JLabel();
        cmbBPP = new javax.swing.JComboBox();
        lblRes = new javax.swing.JLabel();
        cmbRes = new javax.swing.JComboBox();

        setTitle("Graphics Config");
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosed(java.awt.event.WindowEvent evt) {

        lblMode.setText("Select display mode:");


        btnStart.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {

        btnCancel.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {

        cmbQuality.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Lowest", "Performance", "Balanced", "Quality", "High", "Maximum" }));

        lblQuality.setText("Quality: ");

        chkFS.setText("Full screen");
        chkFS.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
        chkFS.setMargin(new java.awt.Insets(0, 0, 0, 0));

        cmbFreq.addItemListener(new java.awt.event.ItemListener() {
            public void itemStateChanged(java.awt.event.ItemEvent evt) {


        lblBPP.setText("Color Depth:");

        cmbBPP.addItemListener(new java.awt.event.ItemListener() {
            public void itemStateChanged(java.awt.event.ItemEvent evt) {


        cmbRes.addItemListener(new java.awt.event.ItemListener() {
            public void itemStateChanged(java.awt.event.ItemEvent evt) {

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
                        .addComponent(cmbRes, javax.swing.GroupLayout.PREFERRED_SIZE, 85, javax.swing.GroupLayout.PREFERRED_SIZE)))
                .addGap(10, 10, 10)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                        .addGap(10, 10, 10)
                        .addComponent(cmbBPP, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addGap(16, 16, 16)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                    .addComponent(cmbFreq, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(lblFreq, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addGap(112, 112, 112))
                    .addComponent(cmbFreq, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(cmbBPP, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(cmbRes, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                        .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
                            .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, 373, Short.MAX_VALUE))
                        .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
                                .addComponent(sep, javax.swing.GroupLayout.DEFAULT_SIZE, 373, Short.MAX_VALUE)
                                .addComponent(sclMode, javax.swing.GroupLayout.DEFAULT_SIZE, 373, Short.MAX_VALUE)))
                        .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
                            .addGap(11, 11, 11)
                            .addComponent(cmbQuality, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 172, Short.MAX_VALUE)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addComponent(sclMode, javax.swing.GroupLayout.DEFAULT_SIZE, 143, Short.MAX_VALUE)
                    .addComponent(cmbQuality, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addComponent(sep, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
    }// </editor-fold>                       

    private void cmbFreqItemStateChanged(java.awt.event.ItemEvent evt) {                                        

    private void cmbBPPItemStateChanged(java.awt.event.ItemEvent evt) {                                       

    private void cmbResItemStateChanged(java.awt.event.ItemEvent evt) {                                       

    private void btnStartActionPerformed(java.awt.event.ActionEvent evt) {                                        
        DisplayMode mode = ((DisplayModeWrapper)lstMode.getSelectedValue()).dm;
        synchronized (activeLock){

    private void btnCancelActionPerformed(java.awt.event.ActionEvent evt) {                                         

    private void formWindowClosed(java.awt.event.WindowEvent evt) {                                 
    // Variables declaration - do not modify                    
    private javax.swing.JButton btnCancel;
    private javax.swing.JButton btnStart;
    private javax.swing.JCheckBox chkFS;
    private javax.swing.JComboBox cmbBPP;
    private javax.swing.JComboBox cmbFreq;
    private javax.swing.JComboBox cmbQuality;
    private javax.swing.JComboBox cmbRes;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JLabel lblBPP;
    private javax.swing.JLabel lblFreq;
    private javax.swing.JLabel lblMode;
    private javax.swing.JLabel lblQuality;
    private javax.swing.JLabel lblRes;
    private javax.swing.JList lstMode;
    private javax.swing.JScrollPane sclMode;
    private javax.swing.JSeparator sep;
    // End of variables declaration                  

Constructor takes GameSettings parameter, create the form, set it to visible (setVisible(true)) then wait until the user clicks "Start" with waitFor(). The provided GameSettings are automatically updated.
Try it out.

hi everybody,

I have another proposal for the Game Settings panel. I would like to see the ActionListener, which is attached to the OK-button, to be attached to the input elements (Renderer, Resolution,…) too. This would enable to just strike the RETURN key to start the game. An alternative would be to set the initial focus to the OK-button.

What do you think?

Here is a patch which does two things:

(1) Focus the OK-Button, so hitting "Enter" starts the game right away.

(2) Making the default close operation DISPOSE_ON_CLOSE (same as "Cancel") instead of DO_NOTHING_ON_CLOSE.

Index: src/com/jmex/editors/swing/settings/
RCS file: /cvs/jme/src/com/jmex/editors/swing/settings/,v
retrieving revision 1.13
diff -u -r1.13
--- src/com/jmex/editors/swing/settings/   15 Dec 2007 14:30:32 -0000   1.13
+++ src/com/jmex/editors/swing/settings/   17 Apr 2008 20:53:43 -0000
@@ -42,6 +42,9 @@
 import java.awt.event.ActionListener;
 import java.awt.event.ItemEvent;
 import java.awt.event.ItemListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowListener;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -409,7 +412,7 @@
    public static final boolean prompt(GameSettings settings, String title) throws InterruptedException {
       final JFrame frame = new JFrame(title);
-      frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
+      frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
       final GameSettingsPanel panel = new GameSettingsPanel(settings);
@@ -448,6 +451,7 @@
       b = new JButton("OK");
+      frame.getRootPane().setDefaultButton(b);
       b = new JButton("Cancel");
@@ -456,7 +460,7 @@
       c.setLayout(new BorderLayout());
       c.add(BorderLayout.CENTER, panel);
       c.add(BorderLayout.SOUTH, bottom);