/* * Copyright (c) 2002-2008 LWJGL Project * 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 'LWJGL' 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 org.lwjgl; import java.io.File; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.nio.ByteBuffer; import java.util.*; import dev.pfaff.unfettered.UnsafeUtil; import static java.lang.invoke.MethodType.methodType; /** *

* Internal library methods *

* * @author Brian Matzon * @version $Revision$ * $Id$ */ public class LWJGLUtil { public static final int PLATFORM_LINUX = 1; public static final int PLATFORM_MACOSX = 2; public static final int PLATFORM_WINDOWS = 3; public static final String PLATFORM_LINUX_NAME = "linux"; public static final String PLATFORM_MACOSX_NAME = "macosx"; public static final String PLATFORM_WINDOWS_NAME = "windows"; private static final MethodHandle MH_findLibrary; static { MethodHandles.Lookup l = UnsafeUtil.getTrustedLookup(); try { MH_findLibrary = l.findVirtual(ClassLoader.class, "findLibrary", methodType(String.class, String.class)); } catch (ReflectiveOperationException e) { throw new RuntimeException("Failed to get a MethodHandle for the findLibrary method", e); } } /** LWJGL Logo - 16 by 16 pixels */ public static final ByteBuffer LWJGLIcon16x16 = loadIcon(LWJGLIconData.ICON_16x16); /** LWJGL Logo - 32 by 32 pixels */ public static final ByteBuffer LWJGLIcon32x32 = loadIcon(LWJGLIconData.ICON_32x32); /** Debug flag. */ public static final boolean DEBUG = getPrivilegedBoolean("org.lwjgl.util.Debug"); public static final boolean CHECKS = !getPrivilegedBoolean("org.lwjgl.util.NoChecks"); private static final int PLATFORM; private static StupidSimpleLogger logger = msg -> { //if (DEBUG) { System.err.println("[LWJGL] " + msg.get()); //} }; public static StupidSimpleLogger logger() { return logger; } public static void logger(StupidSimpleLogger newLogger) { logger = newLogger; } /** * Logs the given message. * * @param msg Message to print * * @deprecated Use {@link #logger} instead. */ @Deprecated public static void log(CharSequence msg) { logger().log(() -> msg.toString()); } static { final String osName = getPrivilegedProperty("os.name"); if ( osName.startsWith("Windows") ) PLATFORM = PLATFORM_WINDOWS; else if ( osName.startsWith("Linux") || osName.startsWith("FreeBSD") || osName.startsWith("OpenBSD") || osName.startsWith("SunOS") || osName.startsWith("Unix") ) PLATFORM = PLATFORM_LINUX; else if ( osName.startsWith("Mac OS X") || osName.startsWith("Darwin") ) PLATFORM = PLATFORM_MACOSX; else throw new LinkageError("Unknown platform: " + osName); } private static ByteBuffer loadIcon(String data) { int len = data.length(); ByteBuffer bb = BufferUtils.createByteBuffer(len); for(int i=0 ; i possible_paths = new ArrayList(); String classloader_path = getPathFromClassLoader(libname, classloader); if (classloader_path != null) { if (DEBUG) { logger().log(() -> "getPathFromClassLoader: Path found: " + classloader_path); } possible_paths.add(classloader_path); } for ( String platform_lib_name : platform_lib_names ) { String lwjgl_classloader_path = getPathFromClassLoader("lwjgl", classloader); if ( lwjgl_classloader_path != null ) { if ( DEBUG ) { logger().log(() -> "getPathFromClassLoader: Path found: " + lwjgl_classloader_path); } possible_paths.add(lwjgl_classloader_path.substring(0, lwjgl_classloader_path.lastIndexOf(File.separator)) + File.separator + platform_lib_name); } // add Installer path String alternative_path = getPrivilegedProperty("org.lwjgl.librarypath"); if ( alternative_path != null ) { possible_paths.add(alternative_path + File.separator + platform_lib_name); } // Add all possible paths from java.library.path String java_library_path = getPrivilegedProperty("java.library.path"); StringTokenizer st = new StringTokenizer(java_library_path, File.pathSeparator); while ( st.hasMoreTokens() ) { String path = st.nextToken(); possible_paths.add(path + File.separator + platform_lib_name); } // TODO: this can be very dangerous (see recent (2022-08) use of completely safe notepad.exe to load a malicious dll) //add current path String current_dir = getPrivilegedProperty("user.dir"); possible_paths.add(current_dir + File.separator + platform_lib_name); //add pure library (no path, let OS search) possible_paths.add(platform_lib_name); } //create needed string array return possible_paths.toArray(new String[possible_paths.size()]); } static void execPrivileged(String... cmd_array) throws Exception { Process process = Runtime.getRuntime().exec(cmd_array); // Close unused streams to make sure the child process won't hang process.getInputStream().close(); process.getOutputStream().close(); process.getErrorStream().close(); } private static String getPrivilegedProperty(String property_name) { return System.getProperty(property_name); } /** Gets a boolean property as a privileged action. */ public static boolean getPrivilegedBoolean(String property_name) { String s = getPrivilegedProperty(property_name); if (s == null || s.equals("false")) return false; if (DEBUG) { if (!s.equals("true")) { logger().log(() -> "Value of boolean property " + property_name + " is not one of [true, false]: " + s); } } return true; } /** Gets a string property as a privileged action. */ public static String getPrivilegedString(String property_name) { return getPrivilegedProperty(property_name); } /** * Gets an integer property as a privileged action. * * @param property_name the integer property name * * @return the property value */ public static Integer getPrivilegedInteger(final String property_name) { String s = getPrivilegedProperty(property_name); if (s == null) return null; return Integer.decode(s); } /** * Gets an integer property as a privileged action. * * @param property_name the integer property name * @param default_val the default value to use if the property is not defined * * @return the property value */ public static int getPrivilegedInteger(final String property_name, final int default_val) { String s = getPrivilegedProperty(property_name); if (s == null) return default_val; return Integer.decode(s); } /** * Tries to locate named library from the current ClassLoader * This method exists because native libraries are loaded from native code, and as such * is exempt from ClassLoader library loading routines. It therefore always fails. * We therefore invoke the protected method of the ClassLoader to see if it can * locate it. * * @param libname Name of library to search for * @param classLoader Classloader to use * @return Absolute path to library if found, otherwise null */ private static String getPathFromClassLoader(final String libname, final ClassLoader classLoader) { if (DEBUG) { logger().log(() -> "getPathFromClassLoader: Searching for '" + libname + '\''); } try { return (String) MH_findLibrary.invokeExact(classLoader, libname); } catch (Throwable e) { logger().log(() -> "getPathFromClassLoader: Failure locating using classloader '" + classLoader + '\'', e); return null; } } /** * Method to determine if the current system is running a version of * Mac OS X better than the given version. This is only useful for Mac OS X * specific code and will not work for any other platform. */ public static boolean isMacOSXEqualsOrBetterThan(int major_required, int minor_required) { String os_version = getPrivilegedProperty("os.version"); int dotI = os_version.indexOf('.'); if (dotI == -1) { if (DEBUG) { logger().log("No '.' delimiter was found in value of 'os.version'"); } return false; } int major; int minor; try { major = Integer.parseInt(os_version, 0, dotI, 10); minor = Integer.parseInt(os_version, dotI + 1, os_version.length(), 10); } catch (Exception e) { logger().log("Exception occurred while trying to determine OS version", e); // Best guess, no return false; } return major > major_required || (major == major_required && minor >= minor_required); } /** * Returns a map of public static final integer fields in the specified classes, to their String representations. * An optional filter can be specified to only include specific fields. The target map may be null, in which * case a new map is allocated and returned. *

* This method is useful when debugging to quickly identify values returned from the AL/GL/CL APIs. * * @param filter the filter to use (optional) * @param target the target map (optional) * @param tokenClasses an array of classes to get tokens from * * @return the token map */ public static Map getClassTokens(final TokenFilter filter, final Map target, final Class ... tokenClasses) { return getClassTokens(filter, target, Arrays.asList(tokenClasses)); } /** * Returns a map of public static final integer fields in the specified classes, to their String representations. * An optional filter can be specified to only include specific fields. The target map may be null, in which * case a new map is allocated and returned. *

* This method is useful when debugging to quickly identify values returned from the AL/GL/CL APIs. * * @param filter the filter to use (optional) * @param target the target map (optional) * @param tokenClasses the classes to get tokens from * * @return the token map */ public static Map getClassTokens(final TokenFilter filter, Map target, final Iterable tokenClasses) { if ( target == null ) target = new HashMap(); final int TOKEN_MODIFIERS = Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL; for ( final Class tokenClass : tokenClasses ) { for ( final Field field : tokenClass.getDeclaredFields() ) { // Get only fields. if ( (field.getModifiers() & TOKEN_MODIFIERS) == TOKEN_MODIFIERS && field.getType() == int.class ) { try { final int value = field.getInt(null); if ( filter != null && !filter.accept(field, value) ) continue; if ( target.containsKey(value) ) // Print colliding tokens in their hex representation. target.put(value, toHexString(value)); else target.put(value, field.getName()); } catch (IllegalAccessException e) { // Ignore } } } } return target; } /** * Returns a string representation of the integer argument as an * unsigned integer in base 16. The string will be uppercase * and will have a leading '0x'. * * @param value the integer value * * @return the hex string representation */ public static String toHexString(final int value) { return "0x" + Integer.toHexString(value).toUpperCase(); } /** Simple interface for Field filtering. */ @FunctionalInterface public interface TokenFilter { /** * Should return true if the specified Field passes the filter. * * @param field the Field to test * @param value the integer value of the field * * @return true if the Field is accepted */ boolean accept(Field field, int value); } }