Skip to content

Commit

Permalink
Merge pull request #6419 from Susko3/use-SDL3-text-editing-on-windows
Browse files Browse the repository at this point in the history
Use SDL3 text editing events on windows
  • Loading branch information
peppy authored Nov 14, 2024
2 parents d733029 + 15c612f commit 316bbce
Show file tree
Hide file tree
Showing 3 changed files with 6 additions and 121 deletions.
4 changes: 2 additions & 2 deletions osu.Framework/Platform/SDL3/SDL3Window.cs
Original file line number Diff line number Diff line change
Expand Up @@ -503,11 +503,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

0 comments on commit 316bbce

Please sign in to comment.