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

Use SDL3 text editing events on windows #6419

Merged
merged 2 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions osu.Framework/Platform/SDL3/SDL3Window.cs
Original file line number Diff line number Diff line change
Expand Up @@ -501,11 +501,11 @@ protected virtual void HandleEvent(SDL_Event e)
break;

case SDL_EventType.SDL_EVENT_TEXT_EDITING:
HandleTextEditingEvent(e.edit);
handleTextEditingEvent(e.edit);
break;

case SDL_EventType.SDL_EVENT_TEXT_INPUT:
HandleTextInputEvent(e.text);
handleTextInputEvent(e.text);
break;

case SDL_EventType.SDL_EVENT_KEYMAP_CHANGED:
Expand Down
12 changes: 4 additions & 8 deletions osu.Framework/Platform/SDL3/SDL3Window_Input.cs
Original file line number Diff line number Diff line change
Expand Up @@ -472,18 +472,18 @@ private void handleMouseMotionEvent(SDL_MouseMotionEvent evtMotion)
MouseMoveRelative?.Invoke(new Vector2(evtMotion.xrel * Scale, evtMotion.yrel * Scale));
}

protected virtual void HandleTextInputEvent(SDL_TextInputEvent evtText)
private void handleTextInputEvent(SDL_TextInputEvent evtText)
{
string? text = evtText.GetText();
Debug.Assert(text != null);
TriggerTextInput(text);
TextInput?.Invoke(text);
}

protected virtual void HandleTextEditingEvent(SDL_TextEditingEvent evtEdit)
private void handleTextEditingEvent(SDL_TextEditingEvent evtEdit)
{
string? text = evtEdit.GetText();
Debug.Assert(text != null);
TriggerTextEditing(text, evtEdit.start, evtEdit.length);
TextEditing?.Invoke(text, evtEdit.start, evtEdit.length);
}

private void handleKeyboardEvent(SDL_KeyboardEvent evtKey)
Expand Down Expand Up @@ -713,15 +713,11 @@ private void updateConfineMode()
/// </summary>
public event Action<string>? TextInput;

protected void TriggerTextInput(string text) => TextInput?.Invoke(text);

/// <summary>
/// Invoked when an IME text editing event occurs.
/// </summary>
public event TextEditingDelegate? TextEditing;

protected void TriggerTextEditing(string text, int start, int length) => TextEditing?.Invoke(text, start, length);

/// <inheritdoc cref="IWindow.KeymapChanged"/>
public event Action? KeymapChanged;

Expand Down
111 changes: 0 additions & 111 deletions osu.Framework/Platform/Windows/SDL3WindowsWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Diagnostics;
using System.Drawing;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using osu.Framework.Allocation;
using osu.Framework.Input.Handlers.Mouse;
using osu.Framework.Platform.SDL3;
using osu.Framework.Platform.Windows.Native;
Expand Down Expand Up @@ -62,36 +59,6 @@ public override void Create()
Native.Input.SetWindowFeedbackSetting(WindowHandle, feedbackType, false);
}

public override unsafe void Run()
{
SDL_SetWindowsMessageHook(&messageHook, ObjectHandle.Handle);
base.Run();
}

[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
private static unsafe SDLBool messageHook(IntPtr userdata, MSG* msg)
{
var handle = new ObjectHandle<SDL3WindowsWindow>(userdata);
if (handle.GetTarget(out SDL3WindowsWindow window))
return window.handleEventFromHook(*msg);

return true;
}

private SDLBool handleEventFromHook(MSG msg)
{
switch (msg.message)
{
case Imm.WM_IME_STARTCOMPOSITION:
case Imm.WM_IME_COMPOSITION:
case Imm.WM_IME_ENDCOMPOSITION:
handleImeMessage(msg.hwnd, msg.message, msg.lParam);
break;
}

return true;
}

protected override void HandleEventFromFilter(SDL_Event evt)
{
switch (evt.Type)
Expand Down Expand Up @@ -125,8 +92,6 @@ private void warpCursorFromFocusLoss()
}
}

#region IME handling

public override void StartTextInput(bool allowIme)
{
base.StartTextInput(allowIme);
Expand All @@ -135,82 +100,6 @@ public override void StartTextInput(bool allowIme)

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

protected override void HandleTextInputEvent(SDL_TextInputEvent evtText)
{
string? sdlResult = evtText.GetText();
Debug.Assert(sdlResult != null);

// 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)
{
// clear the result after this SDL event loop finishes so normal text input isn't blocked.
EventScheduler.AddOnce(() => lastImeResult = null);
return;
}

// also block if there is an ongoing composition (unlikely to occur).
if (imeCompositionActive) return;

base.HandleTextInputEvent(evtText);
}

protected override void HandleTextEditingEvent(SDL_TextEditingEvent evtEdit)
{
// handled by custom logic below
}

/// <summary>
/// Whether IME composition is active.
/// </summary>
/// <remarks>Used for blocking SDL IME results since we handle those ourselves.</remarks>
private bool imeCompositionActive;

/// <summary>
/// The last IME result.
/// </summary>
/// <remarks>
/// Used for blocking SDL IME results since we handle those ourselves.
/// Cleared when the SDL events are blocked.
/// </remarks>
private string? lastImeResult;

private void handleImeMessage(IntPtr hWnd, uint uMsg, long lParam)
{
switch (uMsg)
{
case Imm.WM_IME_STARTCOMPOSITION:
imeCompositionActive = true;
ScheduleEvent(() => TriggerTextEditing(string.Empty, 0, 0));
break;

case Imm.WM_IME_COMPOSITION:
using (var inputContext = new Imm.InputContext(hWnd, lParam))
{
if (inputContext.TryGetImeResult(out string? resultText))
{
lastImeResult = resultText;
ScheduleEvent(() => TriggerTextInput(resultText));
}

if (inputContext.TryGetImeComposition(out string? compositionText, out int start, out int length))
{
ScheduleEvent(() => TriggerTextEditing(compositionText, start, length));
}
}

break;

case Imm.WM_IME_ENDCOMPOSITION:
imeCompositionActive = false;
ScheduleEvent(() => TriggerTextEditing(string.Empty, 0, 0));
break;
}
}

#endregion

protected override void HandleTouchFingerEvent(SDL_TouchFingerEvent evtTfinger)
{
if (evtTfinger.TryGetTouchName(out string? name) && name == "pen")
Expand Down
Loading