Skip to content

Virtual mouse in Godot

A “virtual mouse” in this case is something like using a controller to emulate the mouse.

Note to self: private code link is here.

// Without taking ContentScaleFactor into account, the mouse coordinates you get could be wrong.
var mousePosition = GetViewport().GetMousePosition() * GetTree().Root.ContentScaleFactor;
if (@event.IsActionPressed("ui_page_down"))
{
Input.ParseInputEvent(
new InputEventMouseButton
{
Device = (int)InputEvent.DeviceIdEmulation,
Position = mousePosition,
ButtonIndex = MouseButton.Left,
Pressed = true,
}
);
}
// Do the same thing for ui_page_up but with Pressed = false

…when you press PgDn, it will send a mouse-down event. This on its own is not an entire click! You also need to press PgUp.

public override void _Process(double delta)
{
const int speed = 20;
Vector2 diff = Input.GetVector("cursor_left", "cursor_right", "cursor_up", "cursor_down") * speed;
if (!diff.IsZeroApprox())
{
Vector2 mousePos = GetViewport().GetMousePosition() * GetTree().Root.ContentScaleFactor;
InputEventMouseMotion eventMotion =
new()
{
Device = (int)InputEvent.DeviceIdEmulation,
Position = mousePos,
Relative = diff,
Velocity = diff * (float)delta,
};
// The event is still needed for the sake of MouseEntered/Exited signals
Input.ParseInputEvent(eventMotion);
// This only works on desktop platforms; see https://docs.godotengine.org/en/stable/classes/class_input.html#class-input-method-warp-mouse
Input.WarpMouse(mousePos + diff);
}
}

Click events aren’t making it to a particular window

Section titled “Click events aren’t making it to a particular window”

We had to do work around multiple viewports. I have this comment written in our code:

/// <summary>
/// We keep this around just for <see cref="Popup"/>s. We run with <c>display/window/subwindows/embed_subwindows</c>
/// set to <c>true</c>, which means <see cref="PopupMenu"/>s are not in a separate OS window, but they <em>are</em>
/// in a separate viewport. As a result, two things have to happen:
///
/// <list type="number">
/// <item>We need to reparent the <see cref="VirtualCursor"/> so that it can even get events in the new
/// viewport.</item>
/// <item>We need to get the mouse position according to the original viewport since we embed subwindows.</item>
/// </list>
///
/// If we ever change that setting, we would need to change some code here.
///
/// I couldn't figure out a way to fetch this at runtime after this <see cref="VirtualCursor"/> has been reparented,
/// but that's okay; the original viewport shouldn't change.
/// </summary>
private Viewport _startingViewport = null!;