From 0de8a42a2eade5e527b957263f25e3e906e6df8a Mon Sep 17 00:00:00 2001 From: syeyoung Date: Wed, 28 Jul 2021 00:05:09 +0900 Subject: Cursors! --- .../dungeonsguide/utils/cursor/EnumCursor.java | 46 ++++++ .../dungeonsguide/utils/cursor/Foundation.java | 34 ++++ .../dungeonsguide/utils/cursor/GLCursors.java | 179 +++++++++++++++++++++ .../dungeonsguide/utils/cursor/User32.java | 67 ++++++++ .../syeyoung/dungeonsguide/utils/cursor/X11.java | 37 +++++ 5 files changed, 363 insertions(+) create mode 100644 src/main/java/kr/syeyoung/dungeonsguide/utils/cursor/EnumCursor.java create mode 100644 src/main/java/kr/syeyoung/dungeonsguide/utils/cursor/Foundation.java create mode 100644 src/main/java/kr/syeyoung/dungeonsguide/utils/cursor/GLCursors.java create mode 100644 src/main/java/kr/syeyoung/dungeonsguide/utils/cursor/User32.java create mode 100644 src/main/java/kr/syeyoung/dungeonsguide/utils/cursor/X11.java (limited to 'src/main/java/kr/syeyoung/dungeonsguide/utils') diff --git a/src/main/java/kr/syeyoung/dungeonsguide/utils/cursor/EnumCursor.java b/src/main/java/kr/syeyoung/dungeonsguide/utils/cursor/EnumCursor.java new file mode 100644 index 00000000..caea4d61 --- /dev/null +++ b/src/main/java/kr/syeyoung/dungeonsguide/utils/cursor/EnumCursor.java @@ -0,0 +1,46 @@ +/* + * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod + * Copyright (C) 2021 cyoung06 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package kr.syeyoung.dungeonsguide.utils.cursor; + +import lombok.AllArgsConstructor; +import lombok.Getter; + + +@AllArgsConstructor +@Getter +public enum EnumCursor { + DEFAULT(32512,68,"arrowCursor","arrowCursor"), + POINTING_HAND(32649,60,"pointingHandCursor", "pointingHandCursor"), + OPEN_HAND(32649,60,"openHandCursor","openHandCursor"), + CLOSED_HAND(32646,52,"closedHandCursor","closedHandCursor"), + BEAM_CURSOR(32513, 152, "IBeamCursor", "IBeamCursor"), + RESIZE_LEFT(32644, 70,"resizeLeftCursor", "resizeLeftCursor"), + RESIZE_RIGHT(32644, 96,"resizeRightCursor", "resizeRightCursor"), + RESIZE_LEFT_RIGHT(32644, 108, "resizeLeftRightCursor", "resizeLeftRightCursor"), + RESIZE_UP(32645,138,"resizeUpCursor", "resizeUpCursor"), + RESIZE_DOWN(32645,16,"resizeDownCursor", "resizeDownCursor"), + RESIZE_UP_DOWN(32645,116,"resizeUpDownCursor", "resizeUpDownCursor"), + CROSS(32515, 34,"crosshairCursor", "crosshairCursor"); + + + private int windows; + private int linux; + private String macos; + private String altFileName; +} diff --git a/src/main/java/kr/syeyoung/dungeonsguide/utils/cursor/Foundation.java b/src/main/java/kr/syeyoung/dungeonsguide/utils/cursor/Foundation.java new file mode 100644 index 00000000..995ee101 --- /dev/null +++ b/src/main/java/kr/syeyoung/dungeonsguide/utils/cursor/Foundation.java @@ -0,0 +1,34 @@ +/* + * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod + * Copyright (C) 2021 cyoung06 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package kr.syeyoung.dungeonsguide.utils.cursor; + +import com.sun.jna.Library; +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.Structure; + +public interface Foundation extends Library { + Foundation INSTANCE = (Foundation) Native.loadLibrary("Foundation", + Foundation.class); + + Pointer objc_getClass(String className); + Pointer sel_registerName(String selectorName); + void objc_msgSend(Pointer receiver, Pointer selector, Object... args); + Structure objc_msgSend_stret(Pointer receiver, Pointer selector, Object... args); +} diff --git a/src/main/java/kr/syeyoung/dungeonsguide/utils/cursor/GLCursors.java b/src/main/java/kr/syeyoung/dungeonsguide/utils/cursor/GLCursors.java new file mode 100644 index 00000000..415e82bf --- /dev/null +++ b/src/main/java/kr/syeyoung/dungeonsguide/utils/cursor/GLCursors.java @@ -0,0 +1,179 @@ +/* + * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod + * Copyright (C) 2021 cyoung06 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package kr.syeyoung.dungeonsguide.utils.cursor; + +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import net.minecraft.client.Minecraft; +import net.minecraft.util.MathHelper; +import net.minecraft.util.ResourceLocation; +import org.apache.commons.io.IOUtils; +import org.lwjgl.BufferUtils; +import org.lwjgl.LWJGLException; +import org.lwjgl.LWJGLUtil; +import org.lwjgl.input.Cursor; +import sun.misc.Unsafe; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.nio.IntBuffer; +import java.util.HashMap; +import java.util.Map; + + +public class GLCursors { + private static Unsafe unsafe; + private static Class cursorElement; + private static Constructor constructor; + private static Field cursorField; + + private static Map enumCursorCursorMap = new HashMap<>(); + + + static { + try { + Field f = Unsafe.class.getDeclaredField("theUnsafe"); + f.setAccessible(true); + unsafe = (Unsafe) f.get(null); + cursorElement = Class.forName("org.lwjgl.input.Cursor$CursorElement"); + constructor = cursorElement.getDeclaredConstructor(Object.class, long.class, long.class); + constructor.setAccessible(true); + cursorField = Cursor.class.getDeclaredField("cursors"); + cursorField.setAccessible(true); + } catch (NoSuchFieldException | IllegalAccessException | ClassNotFoundException | NoSuchMethodException e) { + e.printStackTrace(); + } + } + + + static { + setupCursors(); + } + + public static void setupCursors() { + if (enumCursorCursorMap.size() != 0) return; + int platform = LWJGLUtil.getPlatform(); + for (EnumCursor value : EnumCursor.values()) { + Cursor c = null; + try { + switch(platform) { + case LWJGLUtil.PLATFORM_WINDOWS: + if (value.getWindows() == -1) continue; + c = createCursorWindows(value.getWindows()); + break; + case LWJGLUtil.PLATFORM_LINUX: + if (value.getLinux() == -1) continue; + c = createCursorLinux(value.getLinux()); + break; + case LWJGLUtil.PLATFORM_MACOSX: + if (value.getMacos() == null) continue; + c = createCursorMac(value.getMacos()); + break; + } + } catch (Throwable e) { + System.out.println("Error occured while loading cursor: "+value); + e.printStackTrace(); + } + try { + if (c == null) { + int hotspotX = 0, hotspotY = 0; + try { + ResourceLocation cursorinfo = new ResourceLocation("dungeonsguide:cursors/"+value.getAltFileName()+".curinfo"); + String cursorinfoStr = IOUtils.toString(Minecraft.getMinecraft().getResourceManager().getResource(cursorinfo).getInputStream()); + hotspotX = Integer.parseInt(cursorinfoStr.split(":")[0]); + hotspotY = Integer.parseInt(cursorinfoStr.split(":")[1]); + } catch (Throwable t) {t.printStackTrace();} + + ResourceLocation cursor = new ResourceLocation("dungeonsguide:cursors/"+value.getAltFileName()+".png"); + + BufferedImage bufferedImage = ImageIO.read(Minecraft.getMinecraft().getResourceManager().getResource(cursor).getInputStream()); + + + int width = bufferedImage.getWidth(); + int height = bufferedImage.getHeight(); + int effWidth = MathHelper.clamp_int(width, Cursor.getMinCursorSize(), Cursor.getMaxCursorSize()); + int effHeight = MathHelper.clamp_int(height, Cursor.getMinCursorSize(), Cursor.getMaxCursorSize()); + int length = effHeight * effWidth; + IntBuffer intBuffer = BufferUtils.createIntBuffer(length); + for (int i = 0; i < length; i++) { + int x = i % effWidth; + int y = i / effWidth; + if (x >= width || y >= height) { + intBuffer.put(0); + } else { + intBuffer.put(bufferedImage.getRGB(x, height - y - 1)); + } + } + intBuffer.flip(); + c = new Cursor(effWidth, effHeight, hotspotX, hotspotY,1,intBuffer, null); + } + } catch (Throwable e) { + System.out.println("Error occured while loading cursor from resource: "+value); + e.printStackTrace(); + } + if (c != null) enumCursorCursorMap.put(value, c); + } + } + + public static Cursor getCursor(EnumCursor enumCursor) { + return enumCursorCursorMap.get(enumCursor); + } + + private static Cursor createCursorWindows(int cursor) throws LWJGLException, InstantiationException, InvocationTargetException, IllegalAccessException { + User32 user32 = User32.INSTANCE; + Pointer hIcon = user32 + .LoadCursorW(Pointer.NULL, cursor); + long iconPtr = Pointer.nativeValue(hIcon); + + return createCursor(iconPtr); + } + private static Cursor createCursorLinux(int cursor) throws LWJGLException, InstantiationException, InvocationTargetException, IllegalAccessException { + X11.Display display = X11.INSTANCE.XOpenDisplay(null); + Pointer fontCursor = X11.INSTANCE.XCreateFontCursor(display, cursor); + long iconPtr = Pointer.nativeValue(fontCursor); + + return createCursor(iconPtr); + } + private static Cursor createCursorMac(String cursor) throws LWJGLException, InstantiationException, InvocationTargetException, IllegalAccessException { + // trust me, it's horrible. + Foundation foundation = Foundation.INSTANCE; + Pointer nsCursor = foundation.objc_getClass("NSCursor"); + Pointer selector = foundation.sel_registerName(cursor); + Structure structure = foundation.objc_msgSend_stret(nsCursor, selector); + Pointer thePointer = structure.getPointer(); + long iconPtr = Pointer.nativeValue(thePointer); + + return createCursor(iconPtr); + } + + + private static Cursor createCursor(Object handle) throws IllegalAccessException, InvocationTargetException, InstantiationException { + // Yes. I had no way. + Cursor ADANGEROUSOBJECT = (Cursor) unsafe.allocateInstance(Cursor.class); + Object cursorElement = constructor.newInstance(handle, 0, LWJGLUtil.getPlatform() == LWJGLUtil.PLATFORM_LINUX ? -1 : System.currentTimeMillis()); + Object array = Array.newInstance(GLCursors.cursorElement, 1); + Array.set(array, 0, cursorElement); + cursorField.set(ADANGEROUSOBJECT, array); + return ADANGEROUSOBJECT; + } +} diff --git a/src/main/java/kr/syeyoung/dungeonsguide/utils/cursor/User32.java b/src/main/java/kr/syeyoung/dungeonsguide/utils/cursor/User32.java new file mode 100644 index 00000000..a5e594ba --- /dev/null +++ b/src/main/java/kr/syeyoung/dungeonsguide/utils/cursor/User32.java @@ -0,0 +1,67 @@ +/* + * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod + * Copyright (C) 2021 cyoung06 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package kr.syeyoung.dungeonsguide.utils.cursor; + +import com.sun.jna.Library; +import com.sun.jna.Native; +import com.sun.jna.Pointer; + +public interface User32 extends Library { + + public static User32 INSTANCE = (User32) Native + .loadLibrary("User32", User32.class); + + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_ARROW = 32512; + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_IBEAM = 32513; + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_WAIT = 32514; + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_CROSS = 32515; + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_UPARROW = 32516; + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_SIZENWSE = 32642; + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_SIZENESW = 32643; + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_SIZEWE = 32644; + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_SIZENS = 32645; + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_SIZEALL = 32646; + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_NO = 32648; + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_HAND = 32649; + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_APPSTARTING = 32650; + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_HELP = 32651; + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_ICON = 32641; + /** @see #LoadCursorW(Pointer, int) */ + public static final int IDC_SIZE = 32640; + + /** http://msdn.microsoft.com/en-us/library/ms648391(VS.85).aspx */ + public Pointer LoadCursorW(Pointer hInstance, + int lpCursorName); + +} diff --git a/src/main/java/kr/syeyoung/dungeonsguide/utils/cursor/X11.java b/src/main/java/kr/syeyoung/dungeonsguide/utils/cursor/X11.java new file mode 100644 index 00000000..caf8ae10 --- /dev/null +++ b/src/main/java/kr/syeyoung/dungeonsguide/utils/cursor/X11.java @@ -0,0 +1,37 @@ +/* + * Dungeons Guide - The most intelligent Hypixel Skyblock Dungeons Mod + * Copyright (C) 2021 cyoung06 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package kr.syeyoung.dungeonsguide.utils.cursor; + +import com.sun.jna.Library; +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.PointerType; + +public interface X11 extends Library { + X11 INSTANCE = (X11) Native.loadLibrary("X11", X11.class); + public Pointer XCreateFontCursor(Display display, + int shape); + public Display XOpenDisplay(String var1); + + public static class Display extends PointerType { + public Display() { + } + } + +} -- cgit