diff --git a/internal/win32/util.go b/internal/win32/util.go index b8d2c70..5437b0f 100644 --- a/internal/win32/util.go +++ b/internal/win32/util.go @@ -15,26 +15,13 @@ func hiword(x uint32) uint16 { return uint16((x >> 16) & 0xFFFF) } -func decodeUtf16(s uint16) rune { - const ( - // 0xd800-0xdc00 encodes the high 10 bits of a pair. - // 0xdc00-0xe000 encodes the low 10 bits of a pair. - // the value is those 20 bits plus 0x10000. - surr1 = 0xd800 - surr3 = 0xe000 - - // Unicode replacement character - replacementChar = '\uFFFD' - ) +func isSurrogatedCharacter(x rune) bool { + return x > 0xd800 // Surrogate characters are mounted after 0xd800 +} - var a rune - switch r := s; { - case r < surr1, surr3 <= r: - // normal rune - a = rune(r) - default: - // invalid surrogate sequence - a = replacementChar - } - return a +// surrogatedUtf16toRune recovers code points from high and low surrogates +func surrogatedUtf16toRune(high rune, low rune) rune { + high -= 0xd800 + low -= 0xdc00 + return (high << 10) + low + 0x10000 } diff --git a/internal/win32/window.go b/internal/win32/window.go index dc40409..296fb55 100644 --- a/internal/win32/window.go +++ b/internal/win32/window.go @@ -37,6 +37,8 @@ type Window struct { cursorCaptureCount int // non-shared modifiers events.ModifiersState // non-shared + highSurrogated rune // non-shared + // callbacks resizedCb atomicx.Pointer[events.WindowResizedCallback] closeRequestedCb atomicx.Pointer[events.WindowCloseRequestedCallback] @@ -809,11 +811,21 @@ func windowProc(window, msg, wparam, lparam uintptr) uintptr { } } return 0 - case procs.WM_CHAR, procs.WM_SYSCHAR: + // Most UTF16 without surrogates can simply be considered rune. + ch := rune(wparam) + // The surrogated UTF16 character is POSTed as two consecutive WM_CHARs: high surrogate and low surrogate. + if isSurrogatedCharacter(ch) { + if w.highSurrogated == 0 { + w.highSurrogated = ch + return 0 + } + ch = surrogatedUtf16toRune(w.highSurrogated, ch) + w.highSurrogated = 0 + } if cb := w.receivedCharacterCb.Load(); cb != nil { if cb := (*cb); cb != nil { - cb(decodeUtf16(uint16(wparam))) + cb(ch) } } return 0