Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Properly block SDL IME results sent as multiple text events + upstream SDL fixes #5141

Merged
merged 4 commits into from
May 10, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions osu.Framework/Platform/SDL2DesktopWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -215,8 +215,15 @@ public CursorState CursorState

private const int default_icon_size = 256;

/// <summary>
/// Scheduler for actions to run before the next event loop.
/// </summary>
private readonly Scheduler commandScheduler = new Scheduler();
private readonly Scheduler eventScheduler = new Scheduler();

/// <summary>
/// Scheduler for actions to run at the end of the current event loop.
/// </summary>
protected readonly Scheduler EventScheduler = new Scheduler();

private readonly Dictionary<int, SDL2ControllerBindings> controllers = new Dictionary<int, SDL2ControllerBindings>();

Expand Down Expand Up @@ -527,7 +534,7 @@ public void Run()
if (!cursorInWindow.Value)
pollMouse();

eventScheduler.Update();
EventScheduler.Update();

Update?.Invoke();
}
Expand All @@ -554,7 +561,7 @@ private void updateWindowSize()

// This function may be invoked before the SDL internal states are all changed. (as documented here: https://wiki.libsdl.org/SDL_SetEventFilter)
// Scheduling the store to config until after the event poll has run will ensure the window is in the correct state.
eventScheduler.AddOnce(storeWindowSizeToConfig);
EventScheduler.AddOnce(storeWindowSizeToConfig);
}

/// <summary>
Expand Down Expand Up @@ -673,7 +680,7 @@ private void pollMouse()
/// Adds an <see cref="Action"/> to the <see cref="Scheduler"/> expected to handle event callbacks.
/// </summary>
/// <param name="action">The <see cref="Action"/> to execute.</param>
protected void ScheduleEvent(Action action) => eventScheduler.Add(action, false);
protected void ScheduleEvent(Action action) => EventScheduler.Add(action, false);

protected void ScheduleCommand(Action action) => commandScheduler.Add(action, false);

Expand Down
27 changes: 19 additions & 8 deletions osu.Framework/Platform/Windows/WindowsWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using JetBrains.Annotations;
using osu.Framework.Input.Handlers.Mouse;
using osu.Framework.Platform.SDL2;
using osu.Framework.Platform.Windows.Native;
Expand Down Expand Up @@ -105,12 +106,18 @@ public override void StartTextInput(bool allowIme)

public override void ResetIme() => ScheduleCommand(() => Imm.CancelComposition(WindowHandle));

protected override void HandleTextInputEvent(SDL.SDL_TextInputEvent evtText)
protected override unsafe void HandleTextInputEvent(SDL.SDL_TextInputEvent evtText)
{
// block SDL text input if there was a recent result from `handleImeMessage()`.
if (recentImeResult)
if (!SDL2Extensions.TryGetStringFromBytePointer(evtText.text, out string sdlResult))
return;

// Block SDL text input if it was already handled by `handleImeMessage()`.
// SDL truncates text over 32 bytes and sends it as multiple events.
// We assume these events will be handled in the same `pollSDLEvents()` call.
if (lastImeResult?.Contains(sdlResult) == true)
{
recentImeResult = false;
// clear the result after this SDL event loop finishes so normal text input isn't blocked.
EventScheduler.AddOnce(() => lastImeResult = null);
return;
}

Expand All @@ -132,10 +139,14 @@ protected override void HandleTextEditingEvent(SDL.SDL_TextEditingEvent evtEdit)
private bool imeCompositionActive;

/// <summary>
/// Whether an IME result was recently posted.
/// The last IME result.
/// </summary>
/// <remarks>Used for blocking SDL IME results since we handle those ourselves.</remarks>
private bool recentImeResult;
/// <remarks>
/// Used for blocking SDL IME results since we handle those ourselves.
/// Cleared when the SDL events are blocked.
/// </remarks>
[CanBeNull]
private string lastImeResult;

private void handleImeMessage(IntPtr hWnd, uint uMsg, long lParam)
{
Expand All @@ -151,7 +162,7 @@ private void handleImeMessage(IntPtr hWnd, uint uMsg, long lParam)
{
if (inputContext.TryGetImeResult(out string resultText))
{
recentImeResult = true;
lastImeResult = resultText;
ScheduleEvent(() => TriggerTextInput(resultText));
}

Expand Down