1 /* 2 * Copyright (c) 2009-2012 jMonkeyEngine 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 package com.jme3.system; 33 34 import java.io.IOException; 35 import java.io.InputStream; 36 import java.io.OutputStream; 37 import java.io.UnsupportedEncodingException; 38 import java.util.HashMap; 39 import java.util.Map; 40 import java.util.Properties; 41 import java.util.prefs.BackingStoreException; 42 import java.util.prefs.Preferences; 43 44 /** 45 * <code>AppSettings</code> provides a store of configuration 46 * to be used by the application. 47 * <p> 48 * By default only the {@link JmeContext context} uses the configuration, 49 * however the user may set and retrieve the settings as well. 50 * 51 * @author Kirill Vainer 52 */ 53 public final class AppSettings extends HashMap<String, Object> { 54 55 private static final AppSettings defaults = new AppSettings(false); 56 57 /** 58 * Use LWJGL as the display system and force using the OpenGL1.1 renderer. 59 * 60 * @see AppSettings#setRenderer(java.lang.String) 61 */ 62 public static final String LWJGL_OPENGL1 = "LWJGL-OPENGL1"; 63 64 /** 65 * Use LWJGL as the display system and force using the OpenGL2.0 renderer. 66 * <p> 67 * If the underlying system does not support OpenGL2.0, then the context 68 * initialization will throw an exception. 69 * 70 * @see AppSettings#setRenderer(java.lang.String) 71 */ 72 public static final String LWJGL_OPENGL2 = "LWJGL-OpenGL2"; 73 74 /** 75 * Use LWJGL as the display system and force using the core OpenGL3.3 renderer. 76 * <p> 77 * If the underlying system does not support OpenGL3.3, then the context 78 * initialization will throw an exception. Note that currently jMonkeyEngine 79 * does not have any shaders that support OpenGL3.3 therefore this 80 * option is not useful. 81 * 82 * 83 * @see AppSettings#setRenderer(java.lang.String) 84 */ 85 public static final String LWJGL_OPENGL3 = "LWJGL-OpenGL3"; 86 87 /** 88 * Use LWJGL as the display system and allow the context 89 * to choose an appropriate renderer based on system capabilities. 90 * <p> 91 * If the GPU supports OpenGL2 or later, then the OpenGL2.0 renderer will 92 * be used, otherwise, the OpenGL1.1 renderer is used. 93 * 94 * @see AppSettings#setRenderer(java.lang.String) 95 */ 96 public static final String LWJGL_OPENGL_ANY = "LWJGL-OpenGL-Any"; 97 98 /** 99 * The JOGL renderer is no longer supported by jME. 100 * 101 * @deprecated Use the LWJGL renderer instead. 102 * 103 * @see AppSettings#setRenderer(java.lang.String) 104 */ 105 @Deprecated 106 public static final String JOGL = "JOGL"; 107 108 /** 109 * The "NULL" option is no longer supported 110 * 111 * @deprecated Specify the "null" value instead 112 * 113 * @see AppSettings#setRenderer(java.lang.String) 114 * @see AppSettings#setAudioRenderer(java.lang.String) 115 */ 116 @Deprecated 117 public static final String NULL = "NULL"; 118 119 /** 120 * Use the LWJGL OpenAL based renderer for audio capabilities. 121 * 122 * @see AppSettings#setAudioRenderer(java.lang.String) 123 */ 124 public static final String LWJGL_OPENAL = "LWJGL"; 125 126 static { 127 defaults.put("Width", 640); 128 defaults.put("Height", 480); 129 defaults.put("BitsPerPixel", 24); 130 defaults.put("Frequency", 60); 131 defaults.put("DepthBits", 24); 132 defaults.put("StencilBits", 0); 133 defaults.put("Samples", 0); 134 defaults.put("Fullscreen", false); 135 defaults.put("Title", "jMonkey Engine 3.0"); 136 defaults.put("Renderer", LWJGL_OPENGL2); 137 defaults.put("AudioRenderer", LWJGL_OPENAL); 138 defaults.put("DisableJoysticks", true); 139 defaults.put("UseInput", true); 140 defaults.put("VSync", false); 141 defaults.put("FrameRate", -1); 142 defaults.put("SettingsDialogImage", "/com/jme3/app/Monkey.png"); 143 // defaults.put("Icons", null); 144 } 145 146 /** 147 * Create a new instance of <code>AppSettings</code>. 148 * <p> 149 * If <code>loadDefaults</code> is true, then the default settings 150 * will be set on the AppSettings. 151 * Use false if you want to change some settings but you would like the 152 * application to load settings from previous launches. 153 * 154 * @param loadDefaults If default settings are to be loaded. 155 */ AppSettings(boolean loadDefaults)156 public AppSettings(boolean loadDefaults) { 157 if (loadDefaults) { 158 putAll(defaults); 159 } 160 } 161 162 /** 163 * Copies all settings from <code>other</code> to <code>this</code> 164 * AppSettings. 165 * <p> 166 * Any settings that are specified in other will overwrite settings 167 * set on this AppSettings. 168 * 169 * @param other The AppSettings to copy the settings from 170 */ copyFrom(AppSettings other)171 public void copyFrom(AppSettings other) { 172 this.putAll(other); 173 } 174 175 /** 176 * Same as {@link #copyFrom(com.jme3.system.AppSettings) }, except 177 * doesn't overwrite settings that are already set. 178 * 179 * @param other The AppSettings to merge the settings from 180 */ mergeFrom(AppSettings other)181 public void mergeFrom(AppSettings other) { 182 for (String key : other.keySet()) { 183 if (get(key) == null) { 184 put(key, other.get(key)); 185 } 186 } 187 } 188 189 /** 190 * Loads the settings from the given properties input stream. 191 * 192 * @param in The InputStream to load from 193 * @throws IOException If an IOException occurs 194 * 195 * @see #save(java.io.OutputStream) 196 */ load(InputStream in)197 public void load(InputStream in) throws IOException { 198 Properties props = new Properties(); 199 props.load(in); 200 for (Map.Entry<Object, Object> entry : props.entrySet()) { 201 String key = (String) entry.getKey(); 202 String val = (String) entry.getValue(); 203 if (val != null) { 204 val = val.trim(); 205 } 206 if (key.endsWith("(int)")) { 207 key = key.substring(0, key.length() - 5); 208 int iVal = Integer.parseInt(val); 209 putInteger(key, iVal); 210 } else if (key.endsWith("(string)")) { 211 putString(key.substring(0, key.length() - 8), val); 212 } else if (key.endsWith("(bool)")) { 213 boolean bVal = Boolean.parseBoolean(val); 214 putBoolean(key.substring(0, key.length() - 6), bVal); 215 } else { 216 throw new IOException("Cannot parse key: " + key); 217 } 218 } 219 } 220 221 /** 222 * Saves all settings to the given properties output stream. 223 * 224 * @param out The OutputStream to write to 225 * @throws IOException If an IOException occurs 226 * 227 * @see #load(java.io.InputStream) 228 */ save(OutputStream out)229 public void save(OutputStream out) throws IOException { 230 Properties props = new Properties(); 231 for (Map.Entry<String, Object> entry : entrySet()) { 232 Object val = entry.getValue(); 233 String type; 234 if (val instanceof Integer) { 235 type = "(int)"; 236 } else if (val instanceof String) { 237 type = "(string)"; 238 } else if (val instanceof Boolean) { 239 type = "(bool)"; 240 } else { 241 throw new UnsupportedEncodingException(); 242 } 243 props.setProperty(entry.getKey() + type, val.toString()); 244 } 245 props.store(out, "jME3 AppSettings"); 246 } 247 248 /** 249 * Loads settings previously saved in the Java preferences. 250 * 251 * @param preferencesKey The preferencesKey previously used to save the settings. 252 * @throws BackingStoreException If an exception occurs with the preferences 253 * 254 * @see #save(java.lang.String) 255 */ load(String preferencesKey)256 public void load(String preferencesKey) throws BackingStoreException { 257 Preferences prefs = Preferences.userRoot().node(preferencesKey); 258 String[] keys = prefs.keys(); 259 if (keys != null) { 260 for (String key : keys) { 261 Object defaultValue = defaults.get(key); 262 if (defaultValue instanceof Integer) { 263 put(key, prefs.getInt(key, (Integer) defaultValue)); 264 } else if (defaultValue instanceof String) { 265 put(key, prefs.get(key, (String) defaultValue)); 266 } else if (defaultValue instanceof Boolean) { 267 put(key, prefs.getBoolean(key, (Boolean) defaultValue)); 268 } 269 } 270 } 271 } 272 273 /** 274 * Saves settings into the Java preferences. 275 * <p> 276 * On the Windows operating system, the preferences are saved in the registry 277 * at the following key:<br> 278 * <code>HKEY_CURRENT_USER\Software\JavaSoft\Prefs\[preferencesKey]</code> 279 * 280 * @param preferencesKey The preferences key to save at. Generally the 281 * application's unique name. 282 * 283 * @throws BackingStoreException If an exception occurs with the preferences 284 */ save(String preferencesKey)285 public void save(String preferencesKey) throws BackingStoreException { 286 Preferences prefs = Preferences.userRoot().node(preferencesKey); 287 for (String key : keySet()) { 288 prefs.put(key, get(key).toString()); 289 } 290 } 291 292 /** 293 * Get an integer from the settings. 294 * <p> 295 * If the key is not set, then 0 is returned. 296 */ getInteger(String key)297 public int getInteger(String key) { 298 Integer i = (Integer) get(key); 299 if (i == null) { 300 return 0; 301 } 302 303 return i.intValue(); 304 } 305 306 /** 307 * Get a boolean from the settings. 308 * <p> 309 * If the key is not set, then false is returned. 310 */ getBoolean(String key)311 public boolean getBoolean(String key) { 312 Boolean b = (Boolean) get(key); 313 if (b == null) { 314 return false; 315 } 316 317 return b.booleanValue(); 318 } 319 320 /** 321 * Get a string from the settings. 322 * <p> 323 * If the key is not set, then null is returned. 324 */ getString(String key)325 public String getString(String key) { 326 String s = (String) get(key); 327 if (s == null) { 328 return null; 329 } 330 331 return s; 332 } 333 334 /** 335 * Set an integer on the settings. 336 */ putInteger(String key, int value)337 public void putInteger(String key, int value) { 338 put(key, Integer.valueOf(value)); 339 } 340 341 /** 342 * Set a boolean on the settings. 343 */ putBoolean(String key, boolean value)344 public void putBoolean(String key, boolean value) { 345 put(key, Boolean.valueOf(value)); 346 } 347 348 /** 349 * Set a string on the settings. 350 */ putString(String key, String value)351 public void putString(String key, String value) { 352 put(key, value); 353 } 354 355 /** 356 * @param frameRate The frame-rate is the upper limit on how high 357 * the application's frames-per-second can go. 358 * (Default: -1 no frame rate limit imposed) 359 */ setFrameRate(int frameRate)360 public void setFrameRate(int frameRate) { 361 putInteger("FrameRate", frameRate); 362 } 363 364 /** 365 * @param use If true, the application will initialize and use input. 366 * Set to false for headless applications that do not require keyboard 367 * or mouse input. 368 * (Default: true) 369 */ setUseInput(boolean use)370 public void setUseInput(boolean use) { 371 putBoolean("UseInput", use); 372 } 373 374 /** 375 * @param use If true, the application will initialize and use joystick 376 * input. Set to false if no joystick input is desired. 377 * (Default: false) 378 */ setUseJoysticks(boolean use)379 public void setUseJoysticks(boolean use) { 380 putBoolean("DisableJoysticks", !use); 381 } 382 383 /** 384 * Set the graphics renderer to use, one of:<br> 385 * <ul> 386 * <li>AppSettings.LWJGL_OPENGL1 - Force OpenGL1.1 compatability</li> 387 * <li>AppSettings.LWJGL_OPENGL2 - Force OpenGL2 compatability</li> 388 * <li>AppSettings.LWJGL_OPENGL3 - Force OpenGL3.3 compatability</li> 389 * <li>AppSettings.LWJGL_OPENGL_ANY - Choose an appropriate 390 * OpenGL version based on system capabilities</li> 391 * <li>null - Disable graphics rendering</li> 392 * </ul> 393 * @param renderer The renderer to set 394 * (Default: AppSettings.LWJGL_OPENGL2) 395 */ setRenderer(String renderer)396 public void setRenderer(String renderer) { 397 putString("Renderer", renderer); 398 } 399 400 /** 401 * Set a custom graphics renderer to use. The class should implement 402 * the {@link JmeContext} interface. 403 * @param clazz The custom context class. 404 * (Default: not set) 405 */ setCustomRenderer(Class<? extends JmeContext> clazz)406 public void setCustomRenderer(Class<? extends JmeContext> clazz){ 407 put("Renderer", "CUSTOM" + clazz.getName()); 408 } 409 410 /** 411 * Set the audio renderer to use. One of:<br> 412 * <ul> 413 * <li>AppSettings.LWJGL_OPENAL - Default for LWJGL</li> 414 * <li>null - Disable audio</li> 415 * </ul> 416 * @param audioRenderer 417 * (Default: LWJGL) 418 */ setAudioRenderer(String audioRenderer)419 public void setAudioRenderer(String audioRenderer) { 420 putString("AudioRenderer", audioRenderer); 421 } 422 423 /** 424 * @param value the width for the rendering display. 425 * (Default: 640) 426 */ setWidth(int value)427 public void setWidth(int value) { 428 putInteger("Width", value); 429 } 430 431 /** 432 * @param value the height for the rendering display. 433 * (Default: 480) 434 */ setHeight(int value)435 public void setHeight(int value) { 436 putInteger("Height", value); 437 } 438 439 /** 440 * Set the resolution for the rendering display 441 * @param width The width 442 * @param height The height 443 * (Default: 640x480) 444 */ setResolution(int width, int height)445 public void setResolution(int width, int height) { 446 setWidth(width); 447 setHeight(height); 448 } 449 450 /** 451 * Set the frequency, also known as refresh rate, for the 452 * rendering display. 453 * @param value The frequency 454 * (Default: 60) 455 */ setFrequency(int value)456 public void setFrequency(int value) { 457 putInteger("Frequency", value); 458 } 459 460 /** 461 * Sets the number of depth bits to use. 462 * <p> 463 * The number of depth bits specifies the precision of the depth buffer. 464 * To increase precision, specify 32 bits. To decrease precision, specify 465 * 16 bits. On some platforms 24 bits might not be supported, in that case, 466 * specify 16 bits.<p> 467 * (Default: 24) 468 * 469 * @param value The depth bits 470 */ setDepthBits(int value)471 public void setDepthBits(int value){ 472 putInteger("DepthBits", value); 473 } 474 475 /** 476 * Set the number of stencil bits. 477 * <p> 478 * This value is only relevant when the stencil buffer is being used. 479 * Specify 8 to indicate an 8-bit stencil buffer, specify 0 to disable 480 * the stencil buffer. 481 * </p> 482 * (Default: 0) 483 * 484 * @param value Number of stencil bits 485 */ setStencilBits(int value)486 public void setStencilBits(int value){ 487 putInteger("StencilBits", value); 488 } 489 490 /** 491 * Set the bits per pixel for the display. Appropriate 492 * values are 16 for RGB565 color format, or 24 for RGB8 color format. 493 * 494 * @param value The bits per pixel to set 495 * (Default: 24) 496 */ setBitsPerPixel(int value)497 public void setBitsPerPixel(int value) { 498 putInteger("BitsPerPixel", value); 499 } 500 501 /** 502 * Set the number of samples per pixel. A value of 1 indicates 503 * each pixel should be single-sampled, higher values indicate 504 * a pixel should be multi-sampled. 505 * 506 * @param value The number of samples 507 * (Default: 1) 508 */ setSamples(int value)509 public void setSamples(int value) { 510 putInteger("Samples", value); 511 } 512 513 /** 514 * @param title The title of the rendering display 515 * (Default: jMonkeyEngine 3.0) 516 */ setTitle(String title)517 public void setTitle(String title) { 518 putString("Title", title); 519 } 520 521 /** 522 * @param value true to enable full-screen rendering, false to render in a window 523 * (Default: false) 524 */ setFullscreen(boolean value)525 public void setFullscreen(boolean value) { 526 putBoolean("Fullscreen", value); 527 } 528 529 /** 530 * Set to true to enable vertical-synchronization, limiting and synchronizing 531 * every frame rendered to the monitor's refresh rate. 532 * @param value 533 * (Default: false) 534 */ setVSync(boolean value)535 public void setVSync(boolean value) { 536 putBoolean("VSync", value); 537 } 538 539 /** 540 * Enable 3D stereo. 541 * <p>This feature requires hardware support from the GPU driver. 542 * @see <a href="http://en.wikipedia.org/wiki/Quad_buffering">http://en.wikipedia.org/wiki/Quad_buffering</a><br /> 543 * Once enabled, filters or scene processors that handle 3D stereo rendering 544 * could use this feature to render using hardware 3D stereo.</p> 545 * (Default: false) 546 */ setStereo3D(boolean value)547 public void setStereo3D(boolean value){ 548 putBoolean("Stereo3D", value); 549 } 550 551 /** 552 * Sets the application icons to be used, with the most preferred first. 553 * For Windows you should supply at least one 16x16 icon and one 32x32. The former is used for the title/task bar, 554 * the latter for the alt-tab icon. 555 * Linux (and similar platforms) expect one 32x32 icon. 556 * Mac OS X should be supplied one 128x128 icon. 557 * <br/> 558 * The icon is used for the settings window, and the LWJGL render window. Not currently supported for JOGL. 559 * Note that a bug in Java 6 (bug ID 6445278, currently hidden but available in Google cache) currently prevents 560 * the icon working for alt-tab on the settings dialog in Windows. 561 * 562 * @param value An array of BufferedImages to use as icons. 563 * (Default: not set) 564 */ setIcons(Object[] value)565 public void setIcons(Object[] value) { 566 put("Icons", value); 567 } 568 569 /** 570 * Sets the path of the settings dialog image to use. 571 * <p> 572 * The image will be displayed in the settings dialog when the 573 * application is started. 574 * </p> 575 * (Default: /com/jme3/app/Monkey.png) 576 * 577 * @param path The path to the image in the classpath. 578 */ setSettingsDialogImage(String path)579 public void setSettingsDialogImage(String path) { 580 putString("SettingsDialogImage", path); 581 } 582 583 /** 584 * Get the framerate. 585 * @see #setFrameRate(int) 586 */ getFrameRate()587 public int getFrameRate() { 588 return getInteger("FrameRate"); 589 } 590 591 /** 592 * Get the use input state. 593 * @see #setUseInput(boolean) 594 */ useInput()595 public boolean useInput() { 596 return getBoolean("UseInput"); 597 } 598 599 /** 600 * Get the renderer 601 * @see #setRenderer(java.lang.String) 602 */ getRenderer()603 public String getRenderer() { 604 return getString("Renderer"); 605 } 606 607 /** 608 * Get the width 609 * @see #setWidth(int) 610 */ getWidth()611 public int getWidth() { 612 return getInteger("Width"); 613 } 614 615 /** 616 * Get the height 617 * @see #setHeight(int) 618 */ getHeight()619 public int getHeight() { 620 return getInteger("Height"); 621 } 622 623 /** 624 * Get the bits per pixel 625 * @see #setBitsPerPixel(int) 626 */ getBitsPerPixel()627 public int getBitsPerPixel() { 628 return getInteger("BitsPerPixel"); 629 } 630 631 /** 632 * Get the frequency 633 * @see #setFrequency(int) 634 */ getFrequency()635 public int getFrequency() { 636 return getInteger("Frequency"); 637 } 638 639 /** 640 * Get the number of depth bits 641 * @see #setDepthBits(int) 642 */ getDepthBits()643 public int getDepthBits() { 644 return getInteger("DepthBits"); 645 } 646 647 /** 648 * Get the number of stencil bits 649 * @see #setStencilBits(int) 650 */ getStencilBits()651 public int getStencilBits() { 652 return getInteger("StencilBits"); 653 } 654 655 /** 656 * Get the number of samples 657 * @see #setSamples(int) 658 */ getSamples()659 public int getSamples() { 660 return getInteger("Samples"); 661 } 662 663 /** 664 * Get the application title 665 * @see #setTitle(java.lang.String) 666 */ getTitle()667 public String getTitle() { 668 return getString("Title"); 669 } 670 671 /** 672 * Get the vsync state 673 * @see #setVSync(boolean) 674 */ isVSync()675 public boolean isVSync() { 676 return getBoolean("VSync"); 677 } 678 679 /** 680 * Get the fullscreen state 681 * @see #setFullscreen(boolean) 682 */ isFullscreen()683 public boolean isFullscreen() { 684 return getBoolean("Fullscreen"); 685 } 686 687 /** 688 * Get the use joysticks state 689 * @see #setUseJoysticks(boolean) 690 */ useJoysticks()691 public boolean useJoysticks() { 692 return !getBoolean("DisableJoysticks"); 693 } 694 695 /** 696 * Get the audio renderer 697 * @see #setAudioRenderer(java.lang.String) 698 */ getAudioRenderer()699 public String getAudioRenderer() { 700 return getString("AudioRenderer"); 701 } 702 703 /** 704 * Get the stereo 3D state 705 * @see #setStereo3D(boolean) 706 */ useStereo3D()707 public boolean useStereo3D(){ 708 return getBoolean("Stereo3D"); 709 } 710 711 /** 712 * Get the icon array 713 * @see #setIcons(java.lang.Object[]) 714 */ getIcons()715 public Object[] getIcons() { 716 return (Object[]) get("Icons"); 717 } 718 719 /** 720 * Get the settings dialog image 721 * @see #setSettingsDialogImage(java.lang.String) 722 */ getSettingsDialogImage()723 public String getSettingsDialogImage() { 724 return getString("SettingsDialogImage"); 725 } 726 } 727