WoSteff's picture

GameWindow doesn't support non-default hardware cursors

Project:The Open Toolkit library
Version:1.0.0-rc1
Component:Code
Category:bug report
Priority:normal
Assigned:Unassigned
Status:confirmed
Description

A hardware cursor set using Cursor.Current will randomly get reset to the default cursor (Cursors.Default) from within OpenTK's mainloop.
Resetting the cursor each frame to the requested cursor leads to flickering (default cursor shows up for short periods of time)

This is similar behavior to the side effects of Application.DoEvents listed here: http://msdn.microsoft.com/en-us/library/sf27z138.aspx

Using GLControl is probably the only way to get hardware accelerated cursors at the moment.
This is adding quite some totally unneeded overhead especially if you want a fullscreen application.

Software emulating cursors via painting rectangles often is not a viable solution either as this often leads to laggy input.

It'd be useful to either get some way to specify hardware cursors within OpenTK or have OpenTK.Window respect the
current setting rather than destroying it.


Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
the Fiddler's picture

#1

Status:open» confirmed

There is no support for non-default hardware cursors at this point. Since OpenTK.dll cannot take a dependency on System.Windows.Forms, this is not simple to implement. It's on the todo list but relatively low in priority - patches welcome!

the Fiddler's picture

#2

Title:Window mainloop clashes with hardware cursors via Cursor.Current» GameWindow doesn't support non-default hardware cursors

(Note: mixing toolkits may lead to undefined results. OpenTK cannot see inside WinForms and WinForms cannot see inside OpenTK - one is bound to step onto the other's toes sooner or later).

WoSteff's picture

#3

boils down to be the RegisterClassEx with a .Cursor set to non NULL.
Whenever GetMessage is invoked and returns with WM_MOUSEMOVE the cursor gets reset
to the handle supplied by the class default cursor.

MSDN says: "If this member is NULL, an application must explicitly set the cursor shape whenever the mouse moves into the application's window."

Either way keeping it or setting it NULL to allow a HW cursor you'll have to handle WM_SETCURSOR yourself in the following manner.

case WindowMessage.SETCURSOR:
if ((short)((uint)lParam.ToInt32() & 0x0000FFFF) == 1) // HTCLIENT?
{
Functions.SetCursor( user supplied cursor handle would need to go here );
return IntPtr.Zero;
}
break;

Or at least expose some bool that allows logic as:

case WindowMessage.SETCURSOR:
if ((short)((uint)lParam.ToInt32() & 0x0000FFFF) == 1) // HTCLIENT?
{
if (DontResetCursor)
return IntPtr.Zero;
}

This would at least allow an app to (re)set the cursor each frame without it getting reset interframe by any window messages.

the Fiddler's picture

#4

It is possible to work around the issue by hooking into GameWindow.Mouse.Move:

Mouse.Move += (sender, e) =>
{
    Cursor.Current = Cursors.Cross;
};

This solves the issue for me (but a proper solution would still be nice).

WoSteff's picture

#5

Responding the MouseMove unfortunately just reduces the flickering.
I tested with a GetCursor() GetMessage() SetCursor() sequence already. Even this was sometimes not fast enough to catch all visible cursor changes.

I came to a quite effective fix however:

case WindowMessage.SETCURSOR:
if ((short)((uint)lParam.ToInt32() & 0x0000FFFF) == 1) // HTCLIENT?
{
var sender = Functions.InSendMessageEx(IntPtr.Zero);
if (sender == 0)
return IntPtr.Zero;
}
break;
...
case WindowMessage.MOUSEMOVE:
...
if (mouse_outside_window)
{
...
Functions.SetCursor(_defaultCursor);
...
}

with
readonly IntPtr _defaultCursor = Functions.LoadCursor(CursorName.Arrow);

and
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern void SetCursor(IntPtr cursor);

[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern uint InSendMessageEx(IntPtr lpReserved);

This effectivley filters out any automatic cursor changes for the window client area via
if (sender == 0).
The explicit Functions.SetCursor(_defaultCursor); when entering the window passed this check.
In total this (re)sets the cursor only once at all every time the cursor enters the clientarea rather than
resetting it every mouse message, while still honoring nonclient cursors (border sizing etc).

XTZGZoReX's picture

#6

I'm not sure I see why OpenTK cannot depend on System.Windows.Forms. It is a standard assembly implemented both in MS.NET and Mono.

the Fiddler's picture

#7

This bug report is not really the place to discuss this, GameWindow was originally written to avoid the performance pitfalls of WinForms. Moreover, WinForms is certainly not a standard assembly, by any formal definition of 'standard' (and is not supported at all on mobile devices).

iliak's picture

#8

Here's my snippet for opengl hardware cursor : http://www.mimicprod.net/2010/09/hardware-cursor-in-opengl/