Setting a custom hardware cursor does not work unless the mouse cursor re-enters the active game window

Affected versions:

  • 4.17.1
  • 4.17
  • 4.16 (Tested only under Play In Editor functionality)

Description:

Setting a custom hardware cursor does not work unless the mouse cursor enters the active game window. That is, it doesn’t set the cursor when you launch the game (doesn’t matter PIE or standalone) when the cursor is over the active agme window. Even if you click the atcive window, nothing happens. However, if you move the mouse outside of the active game window and then mouse in back in, it will start working correctly and set desired cursor(s) (at this point you can even change the cursors dynamically without re-entering, it will work as intended).

Steps to reproduce:

  1. Download relevant source code from Github as packaged .zip archive.
  2. Run “Setup.bat” and wait for it to finish.
  3. Run “GenerateProjectFiles.bat” and wait for it to finish.
  4. Open generated .sln file in Visual Studio 2015 (Enterprise Update 3, but shouldn’t matter) and build solution.
  5. Launch compiled Unreal Editor.
  6. Create a blank C++ project from the C++ tab by selecting “Baic Code” (No starter content, but shouldn’t matter).
  7. Sub-class GameModeBase as a BluePrint class and set it in Edit -> Project settings -> Maps & Modes -> Default GameMode.
  8. [Optionally] Sub-class PlayerController as a C++ class and add code to set/change hardware cursor dynamically.
  9. Sub-class PlayerController as a BluePrint class and set it in the project settings under earlier set GameMode.
  10. Open “Class Defaults” of the sub-classed BP PlayerController by double clicking the asset in content manager and enable “Show Mouse Cursor”.
  11. Create a directory called “Slate” in projects’ “Content” folder and place desired custom cursor image with .png extension.
  12. Set/add hardware cursor in Edit -> Project settings -> User Interface -> Hardware Cursors, specifying the relative path to your added .png cursor (i.e.: “Slate/cursorFileName”). Change cursor type from “None” to “Default”.
  13. Save level.
  14. “Save all” in content manager.
  15. Click “Compile” and wait for it to finish.
  16. Press “Play” to start a ‘PIE’ (Play In Editor) session and witness the bug.

Hey -

If possible, could you provide a sample project that shows the behavior you’re seeing? When I enter PIE (or standalone) after following the steps above I see no change in my mouse cursor, even if the cursor leaves then reenters the viewport.

I confirm that this indeed happens, for us 4.17.2. Quick workaround - on BeginPlay in PlayerController refresh the cursor and call the WINAPI functions again. I have a custom solution to the entire issue, but basically what I did was calling UGameViewportClient::RebuildCursors, only for hardware type, only for the one single type I needed. More or less what I am actually calling (from within child class of UGameViewportClient):

TSharedPtr<FHardwareCursor> HardwareCursor = HardwareCursors.FindRef(EMouseCursor::Type::Default);
TSharedPtr<ICursor> PlatformCursor = FSlateApplication::Get().GetPlatformCursor();
if ( ICursor* Cursor = PlatformCursor.Get() )
{
	Cursor->SetTypeShape(EMouseCursor::Type::Default, HardwareCursor->GetHandle());
}

EDIT: Too fast :slight_smile: This partially fixes the issue, since something else calls refresh again and clears the custom texture leaving you with windows default hardware cursor again. Generally, the issue is that “bIsMouseOverClient” is initialized to false and when the first time the hardware cursor is set this is still false (and thus if’ed out the actual set call from within UGameViewportClient::SetHardwareCursor) because MouseEnter never comes - since it was always on the viewport. From my understanding this bool should be set appropriately upon viewport Init() call. I’ll dig deeper.

EDIT2: Ok, got it. This code:

#if WITH_EDITOR

	// NOTE: Only do this in editor builds where the editor is running.
	// We don't care about bothering to clear them otherwise, and it may negatively impact
	// things like drag/drop, since those would 'leave' the viewport.
 	if ( !FSlateApplication::Get().IsDragDropping() )
 	{
 		bIsMouseOverClient = false;
 		ResetHardwareCursorStates();
	}

#endif

Causes the cursor to reset to regular windows one. Why? because upon slate tick, the app thinks that the cursor left the viewport, despite the coordinates being exactly the same. It gets a bit more complicated the deeper I venture into Slate, but it seems as if the viewport is created once with some size and has the mouse on itself (without calling MouseEnter), then it gets destructed and a new one is created, with 0x0 size, therefore during this SlateTick it seems as if the widget really lost mouse. I’m not fixing this, I’ll just comment out the entire code I pasted above. I believe someone more competent at Epic should look into this. This is as far as I’ve got (SlateApplication.cpp)

TOptional<FArrangedWidget> FoundWidget = WidgetsUnderPointer.FindArrangedWidget( SomeWidgetPreviouslyUnderCursor.ToSharedRef() );

It does not find the viewport anymore, so the previous viewport gets mouseleave, resetting the textures to windows default ones. Commenting the code (or just using binary without editor) works flawlessly. @OP - cook it and see how it works. of course, you still need to initialize the bIsMouseOverClient property to true, which you cannot do without source. Works flawlessly.

LASTEDIT: As a side note, it would be nice to make a Getter for bUseSoftwareCursorWidgets. There’s only setter :slight_smile:

1 Like

Hey vipeout-

Can you provide a sample project showing the behavior you’re seeing? Following the repro steps initially provided I still have not been able to reproduce an instance that shows the cursor changing/updating upon reentering the game window.

I can try tomorrow. The issue I had (to make sure we’re on the same page) was that the hardware mouse cursor custom texture was not applied, unless I alt-tabbed from the game and returned to it afterwards. I have been testing it in a standalone (-game in commandline).

Unfortunately, this isn’t as easy as it should. I can confirm, that it works in clean 4.17.1 if you just put the hardware cursor and play, both from the editor and -game. I cannot confirm if the specific case I had works in clean 4.17.1 (apparently launcher doesn’t have 4.17.2).

  1. void SetUseSoftwareCursorWidgets(bool bInUseSoftwareCursorWidgets) is not in exposed to blueprints
  2. I don’t see a way of getting a handle to viewport in blueprints at all. In code i use GameInstance for that.

Therefore, I would have to create a new module, make the code there, set it all up the way I have it in my module. I apologize, but that’s going to take time I do not have - if it could be done quickly then sure I’d help you out to get a repro, but this way… well I’m getting paid to create an actual game so I’m gonna have to leave it like that.

My setup:

  • Single hardware cursor in project settings
  • single software cursor in project settings
  • in runtime, depending on the circumstances (input device being used, platform) changing between the two (XBoxOne we use only software cursors, any gamepad device - software ones, mouses / touch - hardware ones), although for the purposes of this issue, it was always called upon viewport Init() function to set software ones to false, after Super:: call (I have my custom viewport class)
  • multiple config changes, which may or may not have something to do with it - I’d have to test those to make sure
  • probably several things that do matter but I cannot think of now.

Is there a proper way to reproduce this workaround in Blueprints? Having the same issue here but didnt touch any code till now.

Cook your project. If it still occurs, you cannot fix this without code changes. If it’s ok, you’re good to go and the issue will persist in editor builds, but end-user will never see it.

100% precisely my case could not be fixed without code changes. The above test will help you determine whether you need code or not.

I can confim this is still happening in 4.18.3, probably in 4.19 too.

We’re still having the issue in 4.18.3 as well.

We are having this issue in 4.19.2. Within the default editor viewport launch mode, there is no issue. But using either the “Standalone game” launch mode, or cooking the whole project, leads to the cursor being initially the default windows cursor, and if the cursor exits the window and comes back in, to our custom hardware cursors.

Fortunately my project is easily simplifiable to a small size (1.4MB zipped). Is there anywhere I can pass it down to you without making it public here?

AnswerHub is not used for bug reporting anymore. Please use bug submission form:
https://epicsupport.force.com/unrealengine/s/
It is also private, so you can attach there your project.
However reproduction in clean project is very simple. Just enable bShowMouseCursor in player controller, set cursor in project settings and play.

Ok. FYI I submitted both scenarios. Case number 7388.

1 Like

I’ve just added a “MouseEnter(Viewport, 0, 0);” to my custom game viewport class’ Init() function, after the Super::Init call. This refreshes the hardware cursors on start and makes sure bIsMouseOverClient is true.

Tested it in standalone and packaged game and it seems to work (4.19.2), without having to modify the engine code. Thanks to vipeout for the research info!

3 Likes

This bug is still driving me crazy in 4.22 : /

As the guy who figured out what’s wrong back then - I’m still using my custom solution and I keep commenting the code each time I upgrade the engine version. Works flawlessly, shipped it and had no complaints on this issue from players.

2 Likes

2021, v4.25.4, hardware cursors still don’t work

Still happens in v4.26.2

“Setting a custom hardware cursor does not work unless the mouse cursor re-enters the active game window.”

check my answer above, still works for me in 4.26

Yes, that’s what I did, thanks! :slight_smile:

It’s a good patch, but Epic should review this issue.