Hardware Cursors (0.7-pre2)

Started by cfmdobbie, August 23, 2003, 10:18:32

Previous topic - Next topic

cfmdobbie

I'm a little confused as to how hardware cursors are used, and unfortunately the example doesn't help much!  Stuff like:

int color_scale = 255/Mouse.getMaxCursorSize();
int bit_mask = 0x81000000;
for (int j = 0; j < image_size; j++) {
    if (j % 4 == 0)
        bit_mask = (~bit_mask) & 0x81000000;
    int color = (j*color_scale/Mouse.getMaxCursorSize()) << 16;
    cursor_images.put(0*image_size + j, 0x00000020 | color | bit_mask);
}


From what I can tell this is doing funky graphical effects, which is great for a demo but sucks as an example! :P  Does anyone have a *simple* example - black and white, one bit transparency, three-line arrow kind of thing?


And now a few questions: The constructor takes an IntBuffer, and as far as I can tell it contains RGBA bytes packed into ints.  Why is this done, as opposed to using a ByteBuffer? *confused*

The hardware cursor starts at (0, 0), and you then track state by polling and storing your own opinion as to where the mouse is.  Doesn't this allow for cursor drift - i.e. getting out of sync with reality?  I understand in the world of dx/dy that you keep track of the position yourself, but in that situation you're drawing the cursor and acting on its position yourself.  If you relinquish control of the first bit, you really need help with the second, don't you?

Is there any way of repositioning the native cursor?


Right, enough whinging, back to the coal face...
ellomynameis Charlie Dobbie.

elias

I might be the only one really using the native cursor (and the implementor), so here goes (there is some information in the Cursor javadocs too):

1. The IntBuffer thing was the most convenient for me, seeing that I only wanted to support that one pixle format (RGBA)

2. The native layer will not allow for drifting because it knows which (dx, dy) to return to get you to match the native cursor position. So as long as you start in (0, 0) everything will be fine

3. No repositioning (yet?).

Sorry about the cryptic "example", which is more precisely described as a stress test to check if everything is working correctly, without an accompanying image. Creating your own should be relatively easy, in fact the pixel format matches that of RGBA textures, and 0, 0 is the lower left corner of the cursor. Be sure to bug me about confusing details, though.

NOTE: If translucency is unsupported (win32), use 0xFF for opaque pixels, and 0x00 for transparent pixels. That way it will work on linux too.

- elias

spasi

Besides no repositioning, there's no way you can hide the native cursor, am I right? That would be the second reason why I'm not using it.

cfmdobbie

Quote from: "elias"1. The IntBuffer thing was the most convenient for me, seeing that I only wanted to support that one pixle format (RGBA)
Ah, hokay.  (Sorry, my mistake - it's ARGB format, not RGBA :oops:)

Quote from: "elias"2. The native layer will not allow for drifting because it knows which (dx, dy) to return to get you to match the native cursor position. So as long as you start in (0, 0) everything will be fine
Hrm.  God forbid you ever forget an update.

Missing a keyboard event is one thing - the user can just press the key again (harder this time :wink:), missing a mouse click isn't a disaster either.  But if you ever forget a movement update (e.g. bad code path) you'll spend the rest of the application with the user clicking half an inch below the mouse cursor.  With no way of knowing... :?  I guess if you're careful it shouldn't be a problem, but it'd be a git to debug! :D

Quote from: "elias"3. No repositioning (yet?).
It's probably no biggie, but always starting the cursor at (0, 0) is a bit icky - it's initially not visible and I've seen people never realise there's mouse support because they couldn't see a cursor...  :roll: :wink:  But you gotta choose somewhere and that's as good as any.

Quote from: "elias"Sorry about the cryptic "example", which is more precisely described as a stress test to check if everything is working correctly, without an accompanying image. Creating your own should be relatively easy, in fact the pixel format matches that of RGBA textures, and 0, 0 is the lower left corner of the cursor. Be sure to bug me about confusing details, though.
Righto, after a bit of playing I'm sure I'll get it working.

...But which leads me on to another question - is there a "nice" way of converting ByteBuffers into packed IntBuffers, RGB into RGBA, RGBA into ARGB etc?  I just can't think of any way of converting pixel formats that doesn't involve the kind of thing I've already got, which is similar to:

if(oldFormat == GL.GL_RGB && newFormat == GL.GL_RGBA) {
  ...
} else if(oldFormat == GL.GL_RGB && newFormat == GL.GL_LUMINANCE) {
  ...
} else if(...


Quote from: "elias"NOTE: If translucency is unsupported (win32), use 0xFF for opaque pixels, and 0x00 for transparent pixels. That way it will work on linux too.
Righto - a good tip!  As for other general guidelines for doing this sort of thing, are hardware cursors well supported - is it a graphics card thing or an operating system thing?  Should we worry "too much" about the native maximum cursor sizes, or is an assumption that 32x32 is supported for most people fair?

Thanks for your response.
ellomynameis Charlie Dobbie.

cfmdobbie

Quote from: "spasi"Besides no repositioning, there's no way you can hide the native cursor, am I right? That would be the second reason why I'm not using it.

Hmmm.  Aside from a transparent Cursor? :?:  Should work fine.
ellomynameis Charlie Dobbie.

cfmdobbie

Quick thought on "cursor drift" - if you know you're in full-screen using native cursors, then you can take advantage of the fact that the cursor keeps within the screen and if your tracking coordinates go out of bounds just adjust them a little.  Voila!  Instant calibration! :D

If a user's cursor starts behaving oddly, the first thing most of them will probably do is waggle it violently anyway. ;)
ellomynameis Charlie Dobbie.

elias

32*32 is a good assumption, because that's the only size win32 supports... Regarding hardware support, I'm not really the authority here. I use Geforce and it just works. And I've seen many main stream games use it (C&C Generals is the latest). My guess is that with reasonably recent hw you'll be allright. However, on linux you'll need a recent X server (>4.3) to display native cursors, as the capabilities field will tell you. In any case a software fallback is a must.

Packing a ByteBuffer into an IntBuffer should be as easy as an asIntBuffer() if you have the pixel data in native byte ordering. Other than that, I'm not really sure if it can be done easier.

Yes, a fully transparent cursor is the way to hide it (even AWT does it that way).

Yeah, missing an update is catastrophic, and has caught me more than once... I'm not sure how to make it more robust without having a query for the absolute cursor position.

- elias

cfmdobbie

Quote from: "elias"In any case a software fallback is a must.
Ah well, I was hoping I'd be able to do away with all that nastiness.  Well, the advantages are such that it's worth including anyway, just in case.

QuotePacking a ByteBuffer into an IntBuffer should be as easy as an asIntBuffer() if you have the pixel data in native byte ordering. Other than that, I'm not really sure if it can be done easier.
Funny you should say that.  When I tried it with an already existing filled ByteBuffer it seemed to return me a 0-length IntBuffer (not much use).  I'm not totally sure of that, as I haven't explored it further, but that's what it seemed. :?

QuoteYes, a fully transparent cursor is the way to hide it (even AWT does it that way).
And from the javadocs you can disable the hardware cursor (sorry, should be calling it a native cursor!) by calling setNativeCursor(null).

QuoteYeah, missing an update is catastrophic, and has caught me more than once... I'm not sure how to make it more robust without having a query for the absolute cursor position.
Nothing apart from that! :wink:

Hrm.  A little worrying entry in the javadoc for setNativeCursor:

* NOTE: The native cursor is not constrained to the window, but
	 * relative events will not be generated if the cursor is outside.


Are they practically unusuable in windowed mode then?  Or do you automatically catch up by virtue of the fact that the first mouse update you receive after the mouse reenters the window is the difference from the last known position, not the last actual position?

EDIT: Hrm, it's gotta be that hasn't it?  What else can Mouse.poll() do? :roll:  Ignore me!
ellomynameis Charlie Dobbie.

elias

Did you remember to rewind to ByteBuffer before you converted it to an IntBuffer?

- elias

cfmdobbie

Hmm.  Possibly not!  I guess a lot of Buffer ops depend on position and limit then?
ellomynameis Charlie Dobbie.

elias

If that's the case, you might be in a few more surprises - LWJGL noW uses position() and limit() where applicable.

- elias

cfmdobbie

Yeah, I've picked up on them (see the "Prerelease 2 of LWJGL 0.7" thread), but I didn't think about things like asIntBuffer() using them.

Time for revising Buffer usage methinks!
ellomynameis Charlie Dobbie.

spasi

When I set a hardware cursor there's a nasty bug when I have dual displays enabled. I've seen it in many commercial games too (e.g. Age of Mythology). When you move the mouse to the screen edge it gets out of the main display and moves to the secondary. After that many BAD things can happen. This doesn't happen without a native cursor. So I wouldn't recommend using it, if possible. There are a lot of users with dual displays nowadays.

cfmdobbie

Arrgh!  Okay, well I think I have a nice, simple native cursor implementation in my toolkit now, but it don't work. :(

My min and max cursor sizes are both 32 pixels, getNativeCursorCaps() includes CURSOR_ONE_BIT_TRANSPARANCY.  My image is the correct size (32x32), contains only 0x00 or 0xff alpha, and is loading and being converted to ARGB correctly.  However, the cursor as displayed contains no transparent sections and flickers once every frame, as if single-buffered.  (It is beautifully frame-rate independent though!)

Now, is this going to be due to LWJGL, WinME or my S3 Savage?  I would guess the Savage.  Ah well, another part of my toolkit to put aside for now.


By the way, CURSOR_ONE_BIT_TRANSPARANCY should be CURSOR_ONE_BIT_TRANSPARENCY.
ellomynameis Charlie Dobbie.

elias

Could you test if the cursor works correctly in windowed mode? And does the lwjgl example work?

Fixed the spelling error, thnx.

- elias