Skip to content

Instantly share code, notes, and snippets.

@dzikoysk
Last active September 8, 2020 11:43
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dzikoysk/0b77744386a659bb7ead629f53830bfd to your computer and use it in GitHub Desktop.
Save dzikoysk/0b77744386a659bb7ead629f53830bfd to your computer and use it in GitHub Desktop.
Clip hardware cursor on Windows using libGDX with LWJGL2 backend, otherwise catch cursor with a standard API
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import org.lwjgl.LWJGLUtil;
import org.lwjgl.opengl.Display;
import org.panda_lang.utilities.commons.function.Lazy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
// LWJGL2 resets mouse clipping every mouse update,
// so we need to call setupCursorClipping() method constantly
// LWJGL3 is not supported due to display impl through the GLFW.
// LWJGL 3 implementation: https://gist.github.com/dzikoysk/b0e2f24048f8fee0ab9253d9d7ab9aa3
public final class Lwjgl2CatchCursorAdapter {
// Lazy class is just a Supplier with cache support
private static final Lazy<Long> HWND = new Lazy<>(() -> {
try {
Method getImplementationMethod = Display.class.getDeclaredMethod("getImplementation");
getImplementationMethod.setAccessible(true);
Object implementation = getImplementationMethod.invoke(null);
Class<?> windowsDisplayClass = Class.forName("org.lwjgl.opengl.WindowsDisplay");
if (!windowsDisplayClass.isInstance(implementation)) {
throw new Exception("The current platform must be Windows!");
}
Method getHwndMethod = windowsDisplayClass.getDeclaredMethod("getHwnd");
getHwndMethod.setAccessible(true);
return (Long) getHwndMethod.invoke(implementation);
} catch (Exception exception) {
throw new RuntimeException(exception);
}
});
private static final Lazy<Method> SETUP_CURSOR_CLIPPING_METHOD = new Lazy<>(() -> {
try {
Class<?> windowsDisplayClass = Class.forName("org.lwjgl.opengl.WindowsDisplay");
Method setupCursorClippingMethod = windowsDisplayClass.getDeclaredMethod("setupCursorClipping", long.class);
setupCursorClippingMethod.setAccessible(true);
return setupCursorClippingMethod;
} catch (Exception exception) {
throw new RuntimeException(exception);
}
});
/**
* Clip cursor manually on Windows
*/
public static void setupCursorClipping() throws InvocationTargetException, IllegalAccessException {
SETUP_CURSOR_CLIPPING_METHOD.get().invoke(null, HWND.get());
}
/**
* Catch cursor with hardware support on Windows.
* The method returns {@link com.badlogic.gdx.ApplicationListener} which should call:
* - create()
* - resume()
* - dispose()
*/
public static ApplicationListener createCatchCursorAdapter() {
if (LWJGLUtil.getPlatform() != LWJGLUtil.PLATFORM_WINDOWS) {
return new ApplicationAdapter() {
@Override
public void create() {
Gdx.input.setCursorCatched(true);
}
@Override
public void dispose() {
Gdx.input.setCursorCatched(false);
}
};
}
// Related to enhanced cursor renderer implementation on Windows
// Details: https://stackoverflow.com/a/16181555/3426515
// Using: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-clipcursor
return new ApplicationAdapter() {
@Override
public void create() {
setupCursorClippingMethod();
}
@Override
public void resume() {
setupCursorClippingMethod();
}
@Override
public void dispose() {
Gdx.input.setCursorCatched(false);
}
private void setupCursorClippingMethod() {
try {
setupCursorClipping();
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
};
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment