Added GLFW error handling and fixed a bug
- GLFW errors are now detected - Added IntConstantsMap - Removed window maximization on init
This commit is contained in:
parent
eadc85bfda
commit
c3bbc8661d
45
src/main/java/ru/windcorp/jputil/ConstantsMapException.java
Normal file
45
src/main/java/ru/windcorp/jputil/ConstantsMapException.java
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* JPUtil
|
||||
* Copyright (C) 2019-2021 OLEGSHA/Javapony and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package ru.windcorp.jputil;
|
||||
|
||||
public class ConstantsMapException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = -4298704891780063127L;
|
||||
|
||||
public ConstantsMapException() {
|
||||
|
||||
}
|
||||
|
||||
public ConstantsMapException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
|
||||
public ConstantsMapException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public ConstantsMapException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ConstantsMapException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
}
|
307
src/main/java/ru/windcorp/jputil/IntConstantsMap.java
Normal file
307
src/main/java/ru/windcorp/jputil/IntConstantsMap.java
Normal file
@ -0,0 +1,307 @@
|
||||
/*
|
||||
* JPUtil
|
||||
* Copyright (C) 2019-2022 OLEGSHA/Javapony and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package ru.windcorp.jputil;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.IntPredicate;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class IntConstantsMap {
|
||||
|
||||
private final Map<Integer, String> namesByValue;
|
||||
private final Map<String, Integer> valuesByName;
|
||||
|
||||
protected IntConstantsMap(Map<Integer, String> namesByValue, Map<String, Integer> valuesByName) {
|
||||
this.namesByValue = namesByValue;
|
||||
this.valuesByName = valuesByName;
|
||||
}
|
||||
|
||||
public int getValue(String name) {
|
||||
Integer value = valuesByName.get(name);
|
||||
if (value == null) {
|
||||
throw new NoSuchElementException("No constant with name " + name);
|
||||
}
|
||||
return value.intValue();
|
||||
}
|
||||
|
||||
public boolean hasConstant(String name) {
|
||||
return valuesByName.containsKey(name);
|
||||
}
|
||||
|
||||
public String getName(int value) {
|
||||
String name = namesByValue.get(value);
|
||||
if (name == null) {
|
||||
throw new NoSuchElementException("No constant with value " + value);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
public boolean hasConstant(int value) {
|
||||
return namesByValue.containsKey(value);
|
||||
}
|
||||
|
||||
public Map<String, Integer> getAll() {
|
||||
return valuesByName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return valuesByName.toString();
|
||||
}
|
||||
|
||||
public static Builder from(Class<?> clazz) {
|
||||
return new Builder(clazz);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
@FunctionalInterface
|
||||
public static interface Filter {
|
||||
boolean test(String name, int value);
|
||||
}
|
||||
|
||||
public class ConstantSpec {
|
||||
public String name;
|
||||
public int value;
|
||||
|
||||
public void drop() {
|
||||
if (!extra.contains(name)) {
|
||||
name = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final List<Consumer<ConstantSpec>> transforms = new ArrayList<>();
|
||||
private final Set<String> extra = new HashSet<>();
|
||||
|
||||
private final Class<?> source;
|
||||
|
||||
public Builder(Class<?> source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public Builder apply(Consumer<ConstantSpec> transform) {
|
||||
transforms.add(transform);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder only(Filter filter) {
|
||||
return apply(s -> {
|
||||
if (!filter.test(s.name, s.value)) {
|
||||
s.drop();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Builder only(Predicate<String> nameFilter) {
|
||||
return apply(s -> {
|
||||
if (!nameFilter.test(s.name)) {
|
||||
s.drop();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Builder onlyValued(IntPredicate valueFilter) {
|
||||
return apply(s -> {
|
||||
if (!valueFilter.test(s.value)) {
|
||||
s.drop();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Builder regex(String regex) {
|
||||
return only(Pattern.compile(regex).asPredicate());
|
||||
}
|
||||
|
||||
public Builder prefix(String prefix) {
|
||||
return only(n -> n.startsWith(prefix) && n.length() > prefix.length());
|
||||
}
|
||||
|
||||
public Builder exclude(Filter filter) {
|
||||
return only((n, v) -> !filter.test(n, v));
|
||||
}
|
||||
|
||||
public Builder exclude(Predicate<String> nameFilter) {
|
||||
return only(nameFilter.negate());
|
||||
}
|
||||
|
||||
public Builder exclude(String... names) {
|
||||
Set<String> excluded = new HashSet<>();
|
||||
for (String name : names) {
|
||||
excluded.add(name);
|
||||
}
|
||||
return exclude(excluded::contains);
|
||||
}
|
||||
|
||||
public Builder excludeRegex(String... nameRegexes) {
|
||||
List<Predicate<String>> tests = new ArrayList<>();
|
||||
for (String regex : nameRegexes) {
|
||||
tests.add(Pattern.compile(regex).asPredicate());
|
||||
}
|
||||
return only((n, v) -> {
|
||||
for (Predicate<String> test : tests) {
|
||||
if (test.test(n)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public Builder extra(String... names) {
|
||||
for (String name : names) {
|
||||
extra.add(name);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder rename(Function<String, String> renamer) {
|
||||
apply(s -> {
|
||||
s.name = renamer.apply(s.name);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder stripPrefix(String prefix) {
|
||||
return apply(s -> {
|
||||
if (s.name.startsWith(prefix)) {
|
||||
s.name = s.name.substring(prefix.length());
|
||||
} else if (extra.contains(s.name)) {
|
||||
return;
|
||||
} else {
|
||||
s.drop();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public IntConstantsMap scan() {
|
||||
return build(true);
|
||||
}
|
||||
|
||||
public IntConstantsMap scanAll() {
|
||||
return build(false);
|
||||
}
|
||||
|
||||
private IntConstantsMap build(boolean onlyPublic) {
|
||||
Map<Integer, String> namesByValue = new HashMap<>();
|
||||
Map<String, Integer> valuesByName = new HashMap<>();
|
||||
|
||||
BiConsumer<String, Integer> putter = (name, value) -> {
|
||||
if (namesByValue.containsKey(value)) {
|
||||
throw newDuplicateException("value", value, name, namesByValue.get(value));
|
||||
}
|
||||
if (valuesByName.containsKey(name)) {
|
||||
throw newDuplicateException("name", name, value, valuesByName.get(name));
|
||||
}
|
||||
namesByValue.put(value, name);
|
||||
valuesByName.put(name, value);
|
||||
};
|
||||
|
||||
try {
|
||||
for (Field field : source.getDeclaredFields()) {
|
||||
processField(field, putter, onlyPublic);
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new ConstantsMapException(e);
|
||||
}
|
||||
|
||||
return new IntConstantsMap(
|
||||
Collections.unmodifiableMap(namesByValue),
|
||||
Collections.unmodifiableMap(valuesByName)
|
||||
);
|
||||
}
|
||||
|
||||
private void processField(Field field, BiConsumer<String, Integer> putter, boolean onlyPublic)
|
||||
throws IllegalAccessException {
|
||||
if (!Modifier.isStatic(field.getModifiers())) {
|
||||
return;
|
||||
}
|
||||
if (!Modifier.isFinal(field.getModifiers())) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean clearAccessible = false;
|
||||
if (!Modifier.isPublic(field.getModifiers())) {
|
||||
if (onlyPublic) {
|
||||
return;
|
||||
} else if (!isAccessibleFlagSet(field)) {
|
||||
field.setAccessible(true);
|
||||
clearAccessible = true;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
ConstantSpec spec = new ConstantSpec();
|
||||
spec.name = field.getName();
|
||||
spec.value = field.getInt(null);
|
||||
|
||||
for (Consumer<ConstantSpec> t : transforms) {
|
||||
t.accept(spec);
|
||||
if (spec.name == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
putter.accept(spec.name, spec.value);
|
||||
|
||||
} finally {
|
||||
if (clearAccessible) {
|
||||
field.setAccessible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Yes, this method exists only so that neither Java 8 nor Java 9 complain about deprecation.
|
||||
*/
|
||||
@Deprecated
|
||||
private boolean isAccessibleFlagSet(Field f) {
|
||||
return f.isAccessible();
|
||||
}
|
||||
|
||||
private ConstantsMapException newDuplicateException(String what, Object common, Object current, Object old) {
|
||||
return new ConstantsMapException(
|
||||
String.format(
|
||||
"Duplicate %1$s: %2$s -> %3$s and %2$s -> %4$s",
|
||||
what,
|
||||
common,
|
||||
current,
|
||||
old
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Progressia
|
||||
* Copyright (C) 2020-2022 Wind Corporation and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package ru.windcorp.progressia.client.graphics.backend;
|
||||
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import org.lwjgl.glfw.GLFWErrorCallback;
|
||||
|
||||
import ru.windcorp.jputil.ConstantsMapException;
|
||||
import ru.windcorp.jputil.IntConstantsMap;
|
||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||
|
||||
public class GLFWErrorHandler {
|
||||
|
||||
private static final IntConstantsMap ERROR_CODES;
|
||||
|
||||
static {
|
||||
try {
|
||||
ERROR_CODES = IntConstantsMap.from(GLFW.class)
|
||||
.stripPrefix("GLFW_")
|
||||
.onlyValued(i -> i >= 0x10000 && i <= 0x1FFFF)
|
||||
.extra("GLFW_NO_ERROR")
|
||||
.scan();
|
||||
} catch (ConstantsMapException e) {
|
||||
throw CrashReports.report(e, "Could not analyze GLFW error codes");
|
||||
}
|
||||
}
|
||||
|
||||
public void onError(int errorCode, long descriptionPointer) {
|
||||
String description = GLFWErrorCallback.getDescription(descriptionPointer);
|
||||
|
||||
String errorCodeName;
|
||||
if (ERROR_CODES.hasConstant(errorCode)) {
|
||||
errorCodeName = ERROR_CODES.getName(errorCode);
|
||||
} else {
|
||||
errorCodeName = "<unknown " + Integer.toHexString(errorCode) + ">";
|
||||
}
|
||||
|
||||
throw CrashReports.report(null, "GLFW error detected: " + errorCodeName + " %s", description);
|
||||
}
|
||||
|
||||
}
|
@ -24,6 +24,7 @@ import static org.lwjgl.system.MemoryUtil.*;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.lwjgl.glfw.GLFWErrorCallback;
|
||||
import org.lwjgl.glfw.GLFWImage;
|
||||
import org.lwjgl.opengl.GL;
|
||||
|
||||
@ -55,6 +56,7 @@ class LWJGLInitializer {
|
||||
setupWindowCallbacks();
|
||||
|
||||
glfwShowWindow(GraphicsBackend.getWindowHandle());
|
||||
GraphicsBackend.onFrameResized(GraphicsBackend.getWindowHandle(), 800, 600);
|
||||
}
|
||||
|
||||
private static void checkEnvironment() {
|
||||
@ -62,8 +64,12 @@ class LWJGLInitializer {
|
||||
}
|
||||
|
||||
private static void initializeGLFW() {
|
||||
// TODO Do GLFW error handling: check glfwInit, setup error callback
|
||||
glfwInit();
|
||||
GLFWErrorCallback.create(new GLFWErrorHandler()::onError).set();
|
||||
|
||||
if (!glfwInit()) {
|
||||
throw CrashReports.report(null, "GLFW could not be initialized: glfwInit() has failed");
|
||||
}
|
||||
|
||||
GraphicsBackend.setGLFWInitialized(true);
|
||||
}
|
||||
|
||||
@ -71,17 +77,13 @@ class LWJGLInitializer {
|
||||
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
|
||||
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
|
||||
glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE);
|
||||
glfwWindowHint(GLFW_MAXIMIZED, GLFW_TRUE);
|
||||
|
||||
long handle = glfwCreateWindow(
|
||||
800,
|
||||
600,
|
||||
Progressia.getName() + " " + Progressia.getFullerVersion(),
|
||||
NULL,
|
||||
NULL
|
||||
);
|
||||
|
||||
// TODO Check that handle != NULL
|
||||
String windowTitle = Progressia.getName() + " " + Progressia.getFullerVersion();
|
||||
long handle = glfwCreateWindow(800, 600, windowTitle, NULL, NULL);
|
||||
|
||||
if (handle == 0) {
|
||||
throw CrashReports.report(null, "Could not create game window");
|
||||
}
|
||||
|
||||
GraphicsBackend.setWindowHandle(handle);
|
||||
|
||||
@ -95,8 +97,8 @@ class LWJGLInitializer {
|
||||
}
|
||||
|
||||
private static void createWindowIcons() {
|
||||
if (glfwGetVersionString().toLowerCase().contains("wayland")) {
|
||||
// glfwSetWindowIcon is not supported on Wayland
|
||||
if (glfwGetPlatform() == GLFW_PLATFORM_WAYLAND) {
|
||||
// Wayland does not support changing window icons
|
||||
return;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user