Set Native cursor image

Is there a simple and useable way to set the Native Cursor image directly?

I don’t want to use softwarecursors, so please don’t suggest them as an alternative.



Any help would be very appreciated :wink:

I found a post somewhere showing how to set a custom native cursor in swing/awt: http://forum.codecall.net/java-tutorials/5504-custom-cursors-using-java.html

it would seem you can only set a cursor per-component in java…

perhaps you could embed your application/game onto a swing/awt canvas, and set the cursor for the frame you use?

Ah, well that is for Awt/Swing and would probably work somehow, however as it seem Lwjgl has a own Mouse system, that would work without that overhead.



With the Mouse class it is possible to set a “native” cursor, however I can’t find any good information how to use it.

Except one old jme1 code example that I faild to convert to current jme api , however this is more or less telling me that it must be possible.



http://lwjgl.org/javadoc/org/lwjgl/opengl/InputImplementation.html



Also Ardor seesm to be able to do something with that (though I have real problems following the byte manipulations, so i currently does not really understand what it does.)



http://ardorlabs.trac.cvsdude.com/Ardor3Dv1/browser/trunk/ardor3d-lwjgl/src/main/java/com/ardor3d/input/lwjgl/LwjglMouseManager.java?rev=994

Solved it :stuck_out_tongue:

Directly ported it from JME2, could do with a clean up, but it works :slight_smile: the logger methods I commented out because I didn’t need them

Example usage:

[java]setHardwareCursor(“Interface/cursor.png”, 0, 0);[/java]

method: (replace {img class=“maximage” src=“http://hub.jmonkeyengine.org/wp-includes/images/smilies/chimpanzee-cool.gif” alt=“8)”} with the alt text, and note that by } and {, I actually mean > and <… damned formatting XD)

[java]

/**

  • Loads and sets a hardware cursor

    *
  • @param url to imagefile
  • @param xHotspot from image left
  • @param yHotspot from image bottom

    */

    public synchronized void setHardwareCursor(String file, int xHotspot, int yHotspot) {

    if (loadedCursors == null) {

    loadedCursors = new HashMap<String, Cursor>();

    }

    final String fileURI = file;

    Cursor cursor = loadedCursors.get(fileURI);

    if (cursor == null) {

    boolean eightBitAlpha =

    (Cursor.getCapabilities() & Cursor.CURSOR_8_BIT_ALPHA) != 0;

    Image image = assetManager.loadTexture(file).getImage();

    boolean isRgba = image.getFormat() == Image.Format.RGBA8;

    int imageWidth = image.getWidth();

    int imageHeight = image.getHeight();

    ByteBuffer imageData = image.getData(0);

    imageData.rewind();

    IntBuffer imageDataCopy = BufferUtils.createIntBuffer(imageWidth * imageHeight);

    for (int y = 0; y < imageHeight; y++) {

    for (int x = 0; x < imageWidth; x++) {

    int index = y * imageWidth + x;

    int r = imageData.get() & 0xff;

    int g = imageData.get() & 0xff;

    int b = imageData.get() & 0xff;

    int a = 0xff;

    if (isRgba) {

    a = imageData.get() & 0xff;

    if (!eightBitAlpha) {

    if (a < 0x7f) {

    a = 0x00;

    // small hack to prevent triggering "reverse screen" on windows.

    r = g = b = 0;

    }

    else {

    a = 0xff;

    }

    }

    }

    imageDataCopy.put(index, (a << 24) | (r << 16) | (g << 8) | b);

    }

    }

    if (xHotspot < 0 || yHotspot < 0

    || xHotspot >= imageWidth

    || yHotspot >= imageHeight) {

    // Revert to a hotspot position of top-left

    xHotspot = 0;

    yHotspot = imageHeight - 1;

    // logger.log(Level.WARNING, "Hotspot positions are outside image bounds!");

    }

    try {

    cursor = new Cursor(imageWidth, imageHeight, xHotspot, yHotspot, 1, imageDataCopy, null);

    } catch (LWJGLException e) {

    // logger.log(Level.WARNING, "Failed creating native cursor!", e);

    }

    loadedCursors.put(fileURI, cursor);

    }

    try {

    if (!cursor.equals(Mouse.getNativeCursor())) {

    Mouse.setNativeCursor(cursor);

    }

    } catch (LWJGLException e) {

    // logger.log(Level.WARNING, "Failed setting native cursor!", e);

    }

    }[/java]
3 Likes

Cool, thanks, guess I will do a small cleanup and then post it here again :wink:

Mostly added a exception to check the size, needed several hours to find out that windows only support multiple of 16 cursors!





public class CursorManager {

static private HashMap<String, Cursor> loadedCursors = new HashMap<String, Cursor>();

private static AssetManager assetManager;



/**

  • Loads and sets a hardware cursor

    *
  • @param url
  •        to imagefile<br />
    
  • @param xHotspot
  •        from image left<br />
    
  • @param yHotspot
  •        from image bottom<br />
    

*/

public CursorManager(AssetManager assetmanager) {

this.assetManager = assetmanager;

}



public static synchronized void precache(String file, int xHotspot,

int yHotspot) {

if (assetManager == null) {

throw new RuntimeException("CursorManager not initialized");

}

boolean eightBitAlpha = (Cursor.getCapabilities() & Cursor.CURSOR_8_BIT_ALPHA) != 0;

Image image = assetManager.loadTexture(file).getImage();

boolean isRgba = image.getFormat() == Image.Format.RGBA8;

int imageWidth = image.getWidth();

int imageHeight = image.getHeight();

if(imageWidth%16 != 0 || imageHeight%16 != 0){

throw new RuntimeException("Cursor must be multiple of 16");

}



ByteBuffer imageData = image.getData(0);

imageData.rewind();

IntBuffer imageDataCopy = BufferUtils.createIntBuffer(imageWidth

  • imageHeight);

    for (int y = 0; y < imageHeight; y++) {

    for (int x = 0; x < imageWidth; x++) {

    int index = y * imageWidth + x;

    int r = imageData.get() & 0xff;

    int g = imageData.get() & 0xff;

    int b = imageData.get() & 0xff;

    int a = 0xff;

    if (isRgba) {

    a = imageData.get() & 0xff;

    if (!eightBitAlpha) {

    if (a < 0x7f) {

    a = 0x00;

    // small hack to prevent triggering "reverse screen"

    // on windows.

    r = g = b = 0;

    } else {

    a = 0xff;

    }

    }

    }

    imageDataCopy.put(index, (a << 24) | (r << 16) | (g << 8) | b);

    }

    }

    if (xHotspot < 0 || yHotspot < 0 || xHotspot >= imageWidth

    || yHotspot >= imageHeight) {

    xHotspot = 0;

    yHotspot = imageHeight - 1;

    }

    try {

    Cursor cursor = new Cursor(imageWidth, imageHeight, xHotspot, yHotspot, 1, imageDataCopy, null);

    loadedCursors.put(file, cursor);

    } catch (LWJGLException e) {

    e.printStackTrace();

    }



    }



    public static synchronized void setHardwareCursor(String file,

    int xHotspot, int yHotspot) {

    System.out.println("Setting cursor " + file);

    final String fileURI = file;

    Cursor cursor = loadedCursors.get(fileURI);

    if (cursor == null) {

    precache(file, xHotspot, yHotspot);

    cursor = loadedCursors.get(fileURI);

    }

    try {

    if (!cursor.equals(Mouse.getNativeCursor())) {

    Mouse.setNativeCursor(cursor);

    }

    } catch (LWJGLException e) {

    e.printStackTrace();

    }

    }

    }



3 Likes

hey this is very interesting. @Kirill don’t you think we should get this in?

Well we can’t just put it in there, it doesn’t fit. Can’t you see?



It implements its own caching not to mention its LWJGL specific.

1 Like

Sorry to be a necromanacer but has any custom cursor control been added to JME3 yet? I’m trying to use the above code but it doesn’t seem to load PNG’s right. It loads jpgs fine, but obviously no alpha. I’ve tried converting the pngs to 8 bit but still nothing.



Forgot to refresh Eclipse. Converting png to 8 bit with pngquant (PngUtils) worked.