diff --git a/jme3-core/src/main/java/com/jme3/system/JmeSystem.java b/jme3-core/src/main/java/com/jme3/system/JmeSystem.java index 675dab19dc..82f1809897 100644 --- a/jme3-core/src/main/java/com/jme3/system/JmeSystem.java +++ b/jme3-core/src/main/java/com/jme3/system/JmeSystem.java @@ -1,125 +1,125 @@ -/* - * Copyright (c) 2009-2025 jMonkeyEngine - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of 'jMonkeyEngine' nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.jme3.system; - +/* + * Copyright (c) 2009-2025 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.system; + import com.jme3.asset.AssetManager; import com.jme3.audio.AudioRenderer; import com.jme3.input.SoftTextDialogInput; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.lang.reflect.Constructor; -import java.net.URL; -import java.nio.ByteBuffer; -import java.util.function.BiFunction; -import java.util.function.Consumer; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Constructor; +import java.net.URL; +import java.nio.ByteBuffer; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** * Utility class to access platform-dependant features. */ public class JmeSystem { private static final Logger logger = Logger.getLogger(JmeSystem.class.getName()); - - public enum StorageFolderType { - Internal, - External, - } - - private static JmeSystemDelegate systemDelegate; - - /** - * A private constructor to inhibit instantiation of this class. - */ - private JmeSystem() { - } - + + public enum StorageFolderType { + Internal, + External, + } + + private static JmeSystemDelegate systemDelegate; + + /** + * A private constructor to inhibit instantiation of this class. + */ + private JmeSystem() { + } + public static void setSystemDelegate(JmeSystemDelegate systemDelegate) { JmeSystem.systemDelegate = systemDelegate; } - public static synchronized File getStorageFolder() { - return getStorageFolder(StorageFolderType.External); - } - - public static synchronized File getStorageFolder(StorageFolderType type) { - checkDelegate(); - return systemDelegate.getStorageFolder(type); - } - - public static String getFullName() { - checkDelegate(); - return systemDelegate.getFullName(); - } - - public static InputStream getResourceAsStream(String name) { - checkDelegate(); - return systemDelegate.getResourceAsStream(name); - } - - public static URL getResource(String name) { - checkDelegate(); - return systemDelegate.getResource(name); - } - - public static boolean trackDirectMemory() { - checkDelegate(); - return systemDelegate.trackDirectMemory(); - } - - public static void setLowPermissions(boolean lowPerm) { - checkDelegate(); - systemDelegate.setLowPermissions(lowPerm); - } - - public static boolean isLowPermissions() { - checkDelegate(); - return systemDelegate.isLowPermissions(); - } - - public static void setSoftTextDialogInput(SoftTextDialogInput input) { - checkDelegate(); - systemDelegate.setSoftTextDialogInput(input); - } - - /** - * Displays or hides the onscreen soft keyboard - * - * @param show If true, the keyboard is displayed, if false, the screen is hidden. - */ + public static synchronized File getStorageFolder() { + return getStorageFolder(StorageFolderType.External); + } + + public static synchronized File getStorageFolder(StorageFolderType type) { + checkDelegate(); + return systemDelegate.getStorageFolder(type); + } + + public static String getFullName() { + checkDelegate(); + return systemDelegate.getFullName(); + } + + public static InputStream getResourceAsStream(String name) { + checkDelegate(); + return systemDelegate.getResourceAsStream(name); + } + + public static URL getResource(String name) { + checkDelegate(); + return systemDelegate.getResource(name); + } + + public static boolean trackDirectMemory() { + checkDelegate(); + return systemDelegate.trackDirectMemory(); + } + + public static void setLowPermissions(boolean lowPerm) { + checkDelegate(); + systemDelegate.setLowPermissions(lowPerm); + } + + public static boolean isLowPermissions() { + checkDelegate(); + return systemDelegate.isLowPermissions(); + } + + public static void setSoftTextDialogInput(SoftTextDialogInput input) { + checkDelegate(); + systemDelegate.setSoftTextDialogInput(input); + } + + /** + * Displays or hides the onscreen soft keyboard + * + * @param show If true, the keyboard is displayed, if false, the screen is hidden. + */ public static void showSoftKeyboard(boolean show) { checkDelegate(); systemDelegate.showSoftKeyboard(show); @@ -148,139 +148,150 @@ public static void stopRumble() { public static SoftTextDialogInput getSoftTextDialogInput() { checkDelegate(); return systemDelegate.getSoftTextDialogInput(); - } - - /** - * Compresses a raw image into a stream. - *

- * The encoding is performed via system libraries. On desktop, the encoding - * is performed via ImageIO, whereas on Android, is done via the - * Bitmap class. - * - * @param outStream The stream where to write the image data. - * @param format The format to use, either "png" or "jpg". - * @param imageData The image data in {@link com.jme3.texture.Image.Format#RGBA8} format. - * @param width The width of the image. - * @param height The height of the image. - * @throws IOException If outStream throws an exception while writing. - */ - public static void writeImageFile(OutputStream outStream, String format, ByteBuffer imageData, int width, int height) throws IOException { - checkDelegate(); - systemDelegate.writeImageFile(outStream, format, imageData, width, height); - } - - public static AssetManager newAssetManager(URL configFile) { - checkDelegate(); - return systemDelegate.newAssetManager(configFile); - } - - public static AssetManager newAssetManager() { - checkDelegate(); - return systemDelegate.newAssetManager(); - } - - /** - * Determine which Platform (operating system and architecture) the - * application is running on. - * - * @return an enum value (not null) - */ - public static Platform getPlatform() { - checkDelegate(); - return systemDelegate.getPlatform(); - } - - public static JmeContext newContext(AppSettings settings, JmeContext.Type contextType) { - checkDelegate(); - return systemDelegate.newContext(settings, contextType); - } - - public static AudioRenderer newAudioRenderer(AppSettings settings) { - checkDelegate(); - return systemDelegate.newAudioRenderer(settings); - } - - public static URL getPlatformAssetConfigURL() { - checkDelegate(); - return systemDelegate.getPlatformAssetConfigURL(); - } - - /** - * Displays an error message to the user in whichever way the context - * feels is appropriate. If this is a headless or an offscreen surface - * context, this method should do nothing. - * - * @param message The error message to display. May contain new line - * characters. - * @deprecated Use JmeSystem.handleErrorMessage(String) instead - */ - @Deprecated - public static void showErrorDialog(String message) { - handleErrorMessage(message); - } - - public static void handleErrorMessage(String message) { - checkDelegate(); - systemDelegate.handleErrorMessage(message); - } - - public static void setErrorMessageHandler(Consumer handler) { - checkDelegate(); - systemDelegate.setErrorMessageHandler(handler); - } - - public static void handleSettings(AppSettings sourceSettings, boolean loadFromRegistry) { - checkDelegate(); - systemDelegate.handleSettings(sourceSettings, loadFromRegistry); - } - - public static void setSettingsHandler(BiFunction handler) { - checkDelegate(); - systemDelegate.setSettingsHandler(handler); - } - - @Deprecated - public static boolean showSettingsDialog(AppSettings sourceSettings, final boolean loadFromRegistry) { - checkDelegate(); - return systemDelegate.showSettingsDialog(sourceSettings, loadFromRegistry); - } - - public static void initialize(AppSettings settings) { - checkDelegate(); - systemDelegate.initialize(settings); - } - - private static final String[] delegateClassNames = { - "com.jme3.system.JmeDesktopSystem", - "com.jme3.system.android.JmeAndroidSystem", - "com.jme3.system.ios.JmeIosSystem" - }; - - private static void checkDelegate() { - if (systemDelegate == null) { - try { - for (String className : delegateClassNames) { - systemDelegate = tryLoadDelegate(className); - if (systemDelegate != null) { - return; // Delegate found and loaded - } - } - // None of the system delegates were found. - logger.log(Level.SEVERE, "Failed to find a JmeSystem delegate!\n" - + "Ensure either desktop or android jME3 jar is in the classpath."); - - } catch (ReflectiveOperationException | IllegalArgumentException ex) { - logger.log(Level.SEVERE, "Failed to create JmeSystem delegate:\n{0}", ex); - } - } - } - - private static JmeSystemDelegate tryLoadDelegate(String className) throws ReflectiveOperationException, IllegalArgumentException { - try { - Constructor c = Class.forName(className).getDeclaredConstructor(); - return (JmeSystemDelegate) c.newInstance(); - } catch (ClassNotFoundException ex) { - return null; - } - } -} + } + + /** + * Compresses a raw image into a stream. + *

+ * The encoding is performed via system libraries. On desktop, the encoding + * is performed via ImageIO, whereas on Android, is done via the + * Bitmap class. + * + * @param outStream The stream where to write the image data. + * @param format The format to use, either "png" or "jpg". + * @param imageData The image data in {@link com.jme3.texture.Image.Format#RGBA8} format. + * @param width The width of the image. + * @param height The height of the image. + * @throws IOException If outStream throws an exception while writing. + */ + public static void writeImageFile(OutputStream outStream, String format, ByteBuffer imageData, int width, int height) throws IOException { + checkDelegate(); + systemDelegate.writeImageFile(outStream, format, imageData, width, height); + } + + public static AssetManager newAssetManager(URL configFile) { + checkDelegate(); + return systemDelegate.newAssetManager(configFile); + } + + public static AssetManager newAssetManager() { + checkDelegate(); + return systemDelegate.newAssetManager(); + } + + /** + * Determine which Platform (operating system and architecture) the + * application is running on. + * + * @return an enum value (not null) + */ + public static Platform getPlatform() { + checkDelegate(); + return systemDelegate.getPlatform(); + } + + /** + * Detects if you are in a Wayland session. + * + * @return {@code true} if you are in a Wayland session, otherwise it will + * be {@code false} + */ + public static boolean isWaylandSession() { + checkDelegate(); + return systemDelegate.isWaylandSession(); + } + + public static JmeContext newContext(AppSettings settings, JmeContext.Type contextType) { + checkDelegate(); + return systemDelegate.newContext(settings, contextType); + } + + public static AudioRenderer newAudioRenderer(AppSettings settings) { + checkDelegate(); + return systemDelegate.newAudioRenderer(settings); + } + + public static URL getPlatformAssetConfigURL() { + checkDelegate(); + return systemDelegate.getPlatformAssetConfigURL(); + } + + /** + * Displays an error message to the user in whichever way the context + * feels is appropriate. If this is a headless or an offscreen surface + * context, this method should do nothing. + * + * @param message The error message to display. May contain new line + * characters. + * @deprecated Use JmeSystem.handleErrorMessage(String) instead + */ + @Deprecated + public static void showErrorDialog(String message) { + handleErrorMessage(message); + } + + public static void handleErrorMessage(String message) { + checkDelegate(); + systemDelegate.handleErrorMessage(message); + } + + public static void setErrorMessageHandler(Consumer handler) { + checkDelegate(); + systemDelegate.setErrorMessageHandler(handler); + } + + public static void handleSettings(AppSettings sourceSettings, boolean loadFromRegistry) { + checkDelegate(); + systemDelegate.handleSettings(sourceSettings, loadFromRegistry); + } + + public static void setSettingsHandler(BiFunction handler) { + checkDelegate(); + systemDelegate.setSettingsHandler(handler); + } + + @Deprecated + public static boolean showSettingsDialog(AppSettings sourceSettings, final boolean loadFromRegistry) { + checkDelegate(); + return systemDelegate.showSettingsDialog(sourceSettings, loadFromRegistry); + } + + public static void initialize(AppSettings settings) { + checkDelegate(); + systemDelegate.initialize(settings); + } + + private static final String[] delegateClassNames = { + "com.jme3.system.JmeDesktopSystem", + "com.jme3.system.android.JmeAndroidSystem", + "com.jme3.system.ios.JmeIosSystem" + }; + + private static void checkDelegate() { + if (systemDelegate == null) { + try { + for (String className : delegateClassNames) { + systemDelegate = tryLoadDelegate(className); + if (systemDelegate != null) { + return; // Delegate found and loaded + } + } + // None of the system delegates were found. + logger.log(Level.SEVERE, "Failed to find a JmeSystem delegate!\n" + + "Ensure either desktop or android jME3 jar is in the classpath."); + + } catch (ReflectiveOperationException | IllegalArgumentException ex) { + logger.log(Level.SEVERE, "Failed to create JmeSystem delegate:\n{0}", ex); + } + } + } + + private static JmeSystemDelegate tryLoadDelegate(String className) throws ReflectiveOperationException, IllegalArgumentException { + try { + Constructor c = Class.forName(className).getDeclaredConstructor(); + return (JmeSystemDelegate) c.newInstance(); + } catch (ClassNotFoundException ex) { + return null; + } + } +} diff --git a/jme3-core/src/main/java/com/jme3/system/JmeSystemDelegate.java b/jme3-core/src/main/java/com/jme3/system/JmeSystemDelegate.java index f77872412f..a163dda8c8 100644 --- a/jme3-core/src/main/java/com/jme3/system/JmeSystemDelegate.java +++ b/jme3-core/src/main/java/com/jme3/system/JmeSystemDelegate.java @@ -1,164 +1,164 @@ -/* - * Copyright (c) 2009-2021 jMonkeyEngine - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * * Neither the name of 'jMonkeyEngine' nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.jme3.system; - -import com.jme3.asset.AssetManager; -import com.jme3.asset.DesktopAssetManager; -import com.jme3.audio.AudioRenderer; -import com.jme3.input.HapticDevice; -import com.jme3.input.SoftTextDialogInput; -import com.jme3.util.res.Resources; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.lang.reflect.InvocationTargetException; -import java.net.URL; -import java.nio.ByteBuffer; -import java.util.EnumMap; -import java.util.Map; -import java.util.function.BiFunction; -import java.util.function.Consumer; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @author Kirill Vainer, normenhansen - */ -public abstract class JmeSystemDelegate implements HapticDevice { - - protected final Logger logger = Logger.getLogger(JmeSystem.class.getName()); - protected boolean initialized = false; - protected boolean lowPermissions = false; - protected Map storageFolders = new EnumMap<>(JmeSystem.StorageFolderType.class); - protected SoftTextDialogInput softTextDialogInput = null; - - protected Consumer errorMessageHandler = (message) -> { - JmeDialogsFactory dialogFactory = null; - try { - dialogFactory = (JmeDialogsFactory)Class.forName("com.jme3.system.JmeDialogsFactoryImpl").getConstructor().newInstance(); - } catch(ClassNotFoundException e){ - logger.warning("JmeDialogsFactory implementation not found."); - } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { - e.printStackTrace(); - } - if(dialogFactory != null) dialogFactory.showErrorDialog(message); - else System.err.println(message); - }; - - protected BiFunction settingsHandler = (settings,loadFromRegistry) -> { - JmeDialogsFactory dialogFactory = null; - try { - dialogFactory = (JmeDialogsFactory)Class.forName("com.jme3.system.JmeDialogsFactoryImpl").getConstructor().newInstance(); - } catch(ClassNotFoundException e){ - logger.warning("JmeDialogsFactory implementation not found."); - } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { - e.printStackTrace(); - } - if(dialogFactory != null) return dialogFactory.showSettingsDialog(settings, loadFromRegistry); - return true; - }; - - public synchronized File getStorageFolder(JmeSystem.StorageFolderType type) { - File storageFolder = null; - - switch (type) { - // Internal and External are currently the same folder - case Internal: - case External: - if (lowPermissions) { - throw new UnsupportedOperationException("File system access restricted"); - } - storageFolder = storageFolders.get(type); - if (storageFolder == null) { - // Initialize storage folder - storageFolder = new File(System.getProperty("user.home"), ".jme3"); - if (!storageFolder.exists()) { - storageFolder.mkdir(); - } - storageFolders.put(type, storageFolder); - } - break; - default: - break; - } - if (storageFolder != null) { - if (logger.isLoggable(Level.FINE)) { - logger.log(Level.FINE, "Storage Folder Path: {0}", storageFolder.getAbsolutePath()); - } - } else { - logger.log(Level.FINE, "Storage Folder not found!"); - } - return storageFolder; - } - - public String getFullName() { - return JmeVersion.FULL_NAME; - } - - public InputStream getResourceAsStream(String name) { - return Resources.getResourceAsStream(name,this.getClass()); - } - - public URL getResource(String name) { - return Resources.getResource(name,this.getClass()); - } - - public boolean trackDirectMemory() { - return false; - } - - public void setLowPermissions(boolean lowPerm) { - lowPermissions = lowPerm; - } - - public boolean isLowPermissions() { - return lowPermissions; - } - - public void setSoftTextDialogInput(SoftTextDialogInput input) { - softTextDialogInput = input; - } - - public SoftTextDialogInput getSoftTextDialogInput() { - return softTextDialogInput; - } - - public boolean isDeviceRumbleSupported() { - return false; - } - +/* + * Copyright (c) 2009-2021 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.system; + +import com.jme3.asset.AssetManager; +import com.jme3.asset.DesktopAssetManager; +import com.jme3.audio.AudioRenderer; +import com.jme3.input.HapticDevice; +import com.jme3.input.SoftTextDialogInput; +import com.jme3.util.res.Resources; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.InvocationTargetException; +import java.net.URL; +import java.nio.ByteBuffer; +import java.util.EnumMap; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * + * @author Kirill Vainer, normenhansen + */ +public abstract class JmeSystemDelegate implements HapticDevice { + + protected final Logger logger = Logger.getLogger(JmeSystem.class.getName()); + protected boolean initialized = false; + protected boolean lowPermissions = false; + protected Map storageFolders = new EnumMap<>(JmeSystem.StorageFolderType.class); + protected SoftTextDialogInput softTextDialogInput = null; + + protected Consumer errorMessageHandler = (message) -> { + JmeDialogsFactory dialogFactory = null; + try { + dialogFactory = (JmeDialogsFactory)Class.forName("com.jme3.system.JmeDialogsFactoryImpl").getConstructor().newInstance(); + } catch(ClassNotFoundException e){ + logger.warning("JmeDialogsFactory implementation not found."); + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { + e.printStackTrace(); + } + if(dialogFactory != null) dialogFactory.showErrorDialog(message); + else System.err.println(message); + }; + + protected BiFunction settingsHandler = (settings,loadFromRegistry) -> { + JmeDialogsFactory dialogFactory = null; + try { + dialogFactory = (JmeDialogsFactory)Class.forName("com.jme3.system.JmeDialogsFactoryImpl").getConstructor().newInstance(); + } catch(ClassNotFoundException e){ + logger.warning("JmeDialogsFactory implementation not found."); + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { + e.printStackTrace(); + } + if(dialogFactory != null) return dialogFactory.showSettingsDialog(settings, loadFromRegistry); + return true; + }; + + public synchronized File getStorageFolder(JmeSystem.StorageFolderType type) { + File storageFolder = null; + + switch (type) { + // Internal and External are currently the same folder + case Internal: + case External: + if (lowPermissions) { + throw new UnsupportedOperationException("File system access restricted"); + } + storageFolder = storageFolders.get(type); + if (storageFolder == null) { + // Initialize storage folder + storageFolder = new File(System.getProperty("user.home"), ".jme3"); + if (!storageFolder.exists()) { + storageFolder.mkdir(); + } + storageFolders.put(type, storageFolder); + } + break; + default: + break; + } + if (storageFolder != null) { + if (logger.isLoggable(Level.FINE)) { + logger.log(Level.FINE, "Storage Folder Path: {0}", storageFolder.getAbsolutePath()); + } + } else { + logger.log(Level.FINE, "Storage Folder not found!"); + } + return storageFolder; + } + + public String getFullName() { + return JmeVersion.FULL_NAME; + } + + public InputStream getResourceAsStream(String name) { + return Resources.getResourceAsStream(name,this.getClass()); + } + + public URL getResource(String name) { + return Resources.getResource(name,this.getClass()); + } + + public boolean trackDirectMemory() { + return false; + } + + public void setLowPermissions(boolean lowPerm) { + lowPermissions = lowPerm; + } + + public boolean isLowPermissions() { + return lowPermissions; + } + + public void setSoftTextDialogInput(SoftTextDialogInput input) { + softTextDialogInput = input; + } + + public SoftTextDialogInput getSoftTextDialogInput() { + return softTextDialogInput; + } + + public boolean isDeviceRumbleSupported() { + return false; + } + @Override public void rumble(float amountHigh, float amountLow, float duration) { } @@ -166,165 +166,176 @@ public void rumble(float amountHigh, float amountLow, float duration) { public final AssetManager newAssetManager(URL configFile) { return new DesktopAssetManager(configFile); } - - public final AssetManager newAssetManager() { - return new DesktopAssetManager(null); - } - - public abstract void writeImageFile(OutputStream outStream, String format, ByteBuffer imageData, int width, int height) throws IOException; - - /** - * Set function to handle errors. - * The default implementation show a dialog if available. - * @param handler Consumer to which the error is passed as String - */ - public void setErrorMessageHandler(Consumer handler){ - errorMessageHandler = handler; - } - - /** - * Internal use only: submit an error to the error message handler - */ - public void handleErrorMessage(String message){ - if(errorMessageHandler != null) errorMessageHandler.accept(message); - } - - /** - * Set a function to handler app settings. - * The default implementation shows a settings dialog if available. - * @param handler handler function that accepts as argument an instance of AppSettings - * to transform and a boolean with the value of true if the settings are expected to be loaded from - * the user registry. The handler function returns false if the configuration is interrupted (eg.the the dialog was closed) - * or true otherwise. - */ - public void setSettingsHandler(BiFunction handler){ - settingsHandler = handler; - } - - /** - * Internal use only: summon the settings handler - */ - public boolean handleSettings(AppSettings settings, boolean loadFromRegistry){ - if(settingsHandler != null) return settingsHandler.apply(settings,loadFromRegistry); - return true; - } - - /** - * @deprecated Use JmeSystemDelegate.handleErrorMessage(String) instead - * @param message - */ - @Deprecated - public void showErrorDialog(String message){ - handleErrorMessage(message); - } - - @Deprecated - public boolean showSettingsDialog(AppSettings settings, boolean loadFromRegistry){ - return handleSettings(settings, loadFromRegistry); - } - - - private boolean is64Bit(String arch) { - switch (arch) { - case "amd64": - case "x86_64": - case "aarch64": - case "arm64": - case "ppc64": - case "universal": - return true; - case "x86": - case "i386": - case "i686": - case "aarch32": - case "arm": - case "armv7": - case "armv7l": - return false; - default: - throw new UnsupportedOperationException("Unsupported architecture: " + arch); - } - } - - private boolean isArmArchitecture(String arch) { - return arch.startsWith("arm") || arch.startsWith("aarch"); - } - - private boolean isX86Architecture(String arch) { - return arch.equals("x86") - || arch.equals("amd64") - || arch.equals("x86_64") - || arch.equals("i386") - || arch.equals("i686") - || arch.equals("universal"); - } - - private UnsupportedOperationException unsupported32Bit(String osName) { - return new UnsupportedOperationException("32-bit " + osName + " is not supported."); - } - - private Platform getWindowsPlatform(String arch, boolean is64) { - if (!is64) { - // no 32-bit version - throw unsupported32Bit("Windows"); - } - if (isArmArchitecture(arch)) return Platform.Windows_ARM64; - if (isX86Architecture(arch)) return Platform.Windows64; - throw new UnsupportedOperationException("Unsupported architecture: " + arch); - } - - private Platform getLinuxPlatform(String arch, boolean is64) { - if (!is64) { - // no 32-bit version - throw unsupported32Bit("Linux"); - } - if (isArmArchitecture(arch)) return Platform.Linux_ARM64; - if (isX86Architecture(arch)) return Platform.Linux64; - throw new UnsupportedOperationException("Unsupported architecture: " + arch); - } - - private Platform getMacPlatform(String arch, boolean is64) { - if (!is64) { - // no 32-bit version - throw unsupported32Bit("macOS"); - } - if (isArmArchitecture(arch)) return Platform.MacOSX_ARM64; - if (isX86Architecture(arch)) return Platform.MacOSX64; - throw new UnsupportedOperationException("Unsupported architecture: " + arch); - } - - public Platform getPlatform() { - String os = System.getProperty("os.name").toLowerCase(); - String arch = System.getProperty("os.arch").toLowerCase(); - boolean is64 = is64Bit(arch); - if (os.contains("windows")) { - return getWindowsPlatform(arch, is64); - } else if (os.contains("linux") || os.contains("freebsd") - || os.contains("sunos") || os.contains("unix")) { - return getLinuxPlatform(arch, is64); - } else if (os.contains("mac os x") || os.contains("darwin")) { - return getMacPlatform(arch, is64); - } else { - throw new UnsupportedOperationException("The specified platform: " + os + " is not supported."); - } - } - - public String getBuildInfo() { - StringBuilder sb = new StringBuilder(); - sb.append("Running on ").append(getFullName()).append("\n"); - sb.append(" * Branch: ").append(JmeVersion.BRANCH_NAME).append("\n"); - sb.append(" * Git Hash: ").append(JmeVersion.GIT_SHORT_HASH).append("\n"); - sb.append(" * Build Date: ").append(JmeVersion.BUILD_DATE); - return sb.toString(); - } - - public abstract URL getPlatformAssetConfigURL(); - - public abstract JmeContext newContext(AppSettings settings, JmeContext.Type contextType); - - public abstract AudioRenderer newAudioRenderer(AppSettings settings); - - public abstract void initialize(AppSettings settings); - - public abstract void showSoftKeyboard(boolean show); -} + + public final AssetManager newAssetManager() { + return new DesktopAssetManager(null); + } + + public abstract void writeImageFile(OutputStream outStream, String format, ByteBuffer imageData, int width, int height) throws IOException; + + /** + * Set function to handle errors. + * The default implementation show a dialog if available. + * @param handler Consumer to which the error is passed as String + */ + public void setErrorMessageHandler(Consumer handler){ + errorMessageHandler = handler; + } + + /** + * Internal use only: submit an error to the error message handler + */ + public void handleErrorMessage(String message){ + if(errorMessageHandler != null) errorMessageHandler.accept(message); + } + + /** + * Set a function to handler app settings. + * The default implementation shows a settings dialog if available. + * @param handler handler function that accepts as argument an instance of AppSettings + * to transform and a boolean with the value of true if the settings are expected to be loaded from + * the user registry. The handler function returns false if the configuration is interrupted (eg.the the dialog was closed) + * or true otherwise. + */ + public void setSettingsHandler(BiFunction handler){ + settingsHandler = handler; + } + + /** + * Internal use only: summon the settings handler + */ + public boolean handleSettings(AppSettings settings, boolean loadFromRegistry){ + if(settingsHandler != null) return settingsHandler.apply(settings,loadFromRegistry); + return true; + } + + /** + * @deprecated Use JmeSystemDelegate.handleErrorMessage(String) instead + * @param message + */ + @Deprecated + public void showErrorDialog(String message){ + handleErrorMessage(message); + } + + @Deprecated + public boolean showSettingsDialog(AppSettings settings, boolean loadFromRegistry){ + return handleSettings(settings, loadFromRegistry); + } + + + private boolean is64Bit(String arch) { + switch (arch) { + case "amd64": + case "x86_64": + case "aarch64": + case "arm64": + case "ppc64": + case "universal": + return true; + case "x86": + case "i386": + case "i686": + case "aarch32": + case "arm": + case "armv7": + case "armv7l": + return false; + default: + throw new UnsupportedOperationException("Unsupported architecture: " + arch); + } + } + + private boolean isArmArchitecture(String arch) { + return arch.startsWith("arm") || arch.startsWith("aarch"); + } + + private boolean isX86Architecture(String arch) { + return arch.equals("x86") + || arch.equals("amd64") + || arch.equals("x86_64") + || arch.equals("i386") + || arch.equals("i686") + || arch.equals("universal"); + } + + private UnsupportedOperationException unsupported32Bit(String osName) { + return new UnsupportedOperationException("32-bit " + osName + " is not supported."); + } + + private Platform getWindowsPlatform(String arch, boolean is64) { + if (!is64) { + // no 32-bit version + throw unsupported32Bit("Windows"); + } + if (isArmArchitecture(arch)) return Platform.Windows_ARM64; + if (isX86Architecture(arch)) return Platform.Windows64; + throw new UnsupportedOperationException("Unsupported architecture: " + arch); + } + + private Platform getLinuxPlatform(String arch, boolean is64) { + if (!is64) { + // no 32-bit version + throw unsupported32Bit("Linux"); + } + if (isArmArchitecture(arch)) return Platform.Linux_ARM64; + if (isX86Architecture(arch)) return Platform.Linux64; + throw new UnsupportedOperationException("Unsupported architecture: " + arch); + } + + private Platform getMacPlatform(String arch, boolean is64) { + if (!is64) { + // no 32-bit version + throw unsupported32Bit("macOS"); + } + if (isArmArchitecture(arch)) return Platform.MacOSX_ARM64; + if (isX86Architecture(arch)) return Platform.MacOSX64; + throw new UnsupportedOperationException("Unsupported architecture: " + arch); + } + + public Platform getPlatform() { + String os = System.getProperty("os.name").toLowerCase(); + String arch = System.getProperty("os.arch").toLowerCase(); + boolean is64 = is64Bit(arch); + if (os.contains("windows")) { + return getWindowsPlatform(arch, is64); + } else if (os.contains("linux") || os.contains("freebsd") + || os.contains("sunos") || os.contains("unix")) { + return getLinuxPlatform(arch, is64); + } else if (os.contains("mac os x") || os.contains("darwin")) { + return getMacPlatform(arch, is64); + } else { + throw new UnsupportedOperationException("The specified platform: " + os + " is not supported."); + } + } + + public boolean isWaylandSession() { + Platform platform = getPlatform(); + if (platform.getOs() == Platform.Os.Linux) { + // The following matches the test GLFW does to enable the Wayland backend. + if ("wayland".equalsIgnoreCase(System.getenv("XDG_SESSION_TYPE")) && System.getenv("WAYLAND_DISPLAY") != null) { + return true; + } + } + return false; + } + + public String getBuildInfo() { + StringBuilder sb = new StringBuilder(); + sb.append("Running on ").append(getFullName()).append("\n"); + sb.append(" * Branch: ").append(JmeVersion.BRANCH_NAME).append("\n"); + sb.append(" * Git Hash: ").append(JmeVersion.GIT_SHORT_HASH).append("\n"); + sb.append(" * Build Date: ").append(JmeVersion.BUILD_DATE); + return sb.toString(); + } + + public abstract URL getPlatformAssetConfigURL(); + + public abstract JmeContext newContext(AppSettings settings, JmeContext.Type contextType); + + public abstract AudioRenderer newAudioRenderer(AppSettings settings); + + public abstract void initialize(AppSettings settings); + + public abstract void showSoftKeyboard(boolean show); +} diff --git a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglCanvas.java b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglCanvas.java index 3e286dd112..c35477e993 100644 --- a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglCanvas.java +++ b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglCanvas.java @@ -41,6 +41,7 @@ import com.jme3.system.AppSettings; import com.jme3.system.Displays; import com.jme3.system.JmeCanvasContext; +import com.jme3.system.JmeSystem; import com.jme3.system.lwjglx.LwjglxGLPlatform; import java.awt.AWTException; @@ -465,7 +466,7 @@ protected String getCurrentVideoDriver() { .append('.') .append(canvas.data.minorVersion); - String driver = isWayland() ? "(XWayland|X11) GLX" : "X11 GLX"; + String driver = JmeSystem.isWaylandSession() ? "(XWayland|X11) GLX" : "X11 GLX"; Platform platform = Platform.get(); if (null == platform) { @@ -703,14 +704,16 @@ protected void destroyContext() { */ @Override protected void createContext(AppSettings settings) { - if (!settings.isX11PlatformPreferred() && isWayland()) { + boolean linux = Platform.get() == Platform.LINUX || + Platform.get() == Platform.FREEBSD; + if (!settings.isX11PlatformPreferred() && linux && JmeSystem.isWaylandSession()) { LOGGER.log(Level.WARNING, "LWJGLX and AWT/Swing only work with X11, so XWayland will be used for GLX."); } // HACK: For LWJGLX to work in Wyland, it is necessary to use GLX via // XWayland, so LWJGL must be forced to load GLX as a native API. // This is because LWJGLX does not provide an EGL context. - if (isWayland()) { + if (linux && JmeSystem.isWaylandSession()) { Configuration.OPENGL_CONTEXT_API.set("native"); } diff --git a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java index 13144debbb..4f7866f969 100644 --- a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java +++ b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjgl/LwjglWindow.java @@ -438,10 +438,15 @@ private int[] getDisplayOrigin() { private void configureVideoDriverHints(AppSettings settings) { if (org.lwjgl.system.Platform.get() == org.lwjgl.system.Platform.LINUX) { - boolean isWaylandSession = "wayland".equalsIgnoreCase(System.getenv("XDG_SESSION_TYPE")); + + /* + * Determine whether you want to use X11 or Wayland platform drivers. + * This only works if you are in a Wayland session and want to force + * the use of X11 drivers through XWayland. + */ if (settings.isX11PlatformPreferred()) { SDL_SetHint(SDL_HINT_VIDEO_DRIVER, "x11"); - } else if (isWaylandSession) { + } else if (JmeSystem.isWaylandSession()) { SDL_SetHint(SDL_HINT_VIDEO_DRIVER, "wayland"); } } @@ -498,7 +503,7 @@ private void configureGLAttributes(AppSettings settings) { if (org.lwjgl.system.Platform.get() == org.lwjgl.system.Platform.LINUX) { if (settings.isX11PlatformPreferred()) { SDL_GL_SetAttribute(SDL_GL_EGL_PLATFORM, EGL_PLATFORM_X11_EXT); - } else if ("wayland".equalsIgnoreCase(System.getenv("XDG_SESSION_TYPE"))) { + } else if (JmeSystem.isWaylandSession()) { SDL_GL_SetAttribute(SDL_GL_EGL_PLATFORM, EGL_PLATFORM_WAYLAND_EXT); } } diff --git a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjglx/LwjglxDefaultGLPlatform.java b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjglx/LwjglxDefaultGLPlatform.java index c7058ffcc2..aacda06818 100644 --- a/jme3-lwjgl3/src/main/java/com/jme3/system/lwjglx/LwjglxDefaultGLPlatform.java +++ b/jme3-lwjgl3/src/main/java/com/jme3/system/lwjglx/LwjglxDefaultGLPlatform.java @@ -45,8 +45,11 @@ public final class LwjglxDefaultGLPlatform { /** * Detects if you are in a Wayland session. * + * @deprecated Use {@link com.jme3.system.JmeSystem#isWaylandSession() } + * * @return boolean */ + @Deprecated public static boolean isWayland() { Platform platform = Platform.get(); if (platform == LINUX || platform == FREEBSD) {