Skip to content

Instantly share code, notes, and snippets.

@dzikoysk
Last active September 8, 2020 11:51
Show Gist options
  • Save dzikoysk/b0e2f24048f8fee0ab9253d9d7ab9aa3 to your computer and use it in GitHub Desktop.
Save dzikoysk/b0e2f24048f8fee0ab9253d9d7ab9aa3 to your computer and use it in GitHub Desktop.
Clip hardware cursor on Windows using libGDX with LWJGL3 backend, otherwise catch cursor with a standard API
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application;
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Window;
import com.badlogic.gdx.utils.Array;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.POINT;
import com.sun.jna.platform.win32.WinDef.RECT;
import org.lwjgl.LWJGLUtil;
import org.lwjgl.glfw.GLFWNativeWin32;
import org.panda_lang.utilities.commons.function.Lazy;
import java.lang.reflect.Field;
// LWJGL3 (glfw based) implementation of cursor clipping for hardware cursor on Windows
// LWJGL2 version: https://gist.github.com/dzikoysk/0b77744386a659bb7ead629f53830bfd
// Dependencies: JNA + JNA Platform
public final class Lwjgl3CatchCursorAdapter {
public interface GLFWUser32 extends User32 {
BOOL ClientToScreen(HWND hWnd, POINT lpPoint);
BOOL ClipCursor(RECT rect);
}
// Lazy class is just a Supplier with cache support
private static final Lazy<GLFWUser32> INSTANCE = new Lazy<>(() -> Native.loadLibrary("user32", GLFWUser32.class));
@SuppressWarnings("unchecked")
private static final Lazy<Long> HWND = new Lazy<>(() -> {
try {
Lwjgl3Application application = (Lwjgl3Application) Gdx.app;
Field windowsField = application.getClass().getDeclaredField("windows");
windowsField.setAccessible(true);
Array<Lwjgl3Window> windows = (Array<Lwjgl3Window>) windowsField.get(application);
Lwjgl3Window window = windows.first();
return GLFWNativeWin32.glfwGetWin32Window(window.getWindowHandle());
} catch (Exception exception) {
throw new RuntimeException(exception);
}
});
/**
* Clip cursor manually on Windows
*/
public static void setupCursorClipping() {
HWND hWnd = new HWND(new Pointer(HWND.get()));
RECT rect = new RECT();
INSTANCE.get().GetClientRect(hWnd, rect);
POINT.ByReference leftPointReference = new POINT.ByReference(rect.left, rect.top);
INSTANCE.get().ClientToScreen(hWnd, leftPointReference);
rect.left = leftPointReference.x;
rect.top = leftPointReference.y;
POINT.ByReference rightPointReference = new POINT.ByReference(rect.right, rect.bottom);
INSTANCE.get().ClientToScreen(hWnd, rightPointReference);
rect.right = rightPointReference.x;
rect.bottom = rightPointReference.y;
INSTANCE.get().ClipCursor(rect);
}
/**
* 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() {
setupCursorClipping();
}
@Override
public void resume() {
setupCursorClipping();
}
@Override
public void dispose() {
Gdx.input.setCursorCatched(false);
}
};
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment