mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-09-19 21:02:18 +02:00
internal/ui: disable IME and enable it only when necessary
Closes #2918
This commit is contained in:
parent
5f595efef0
commit
42209606b1
@ -15,6 +15,7 @@
|
|||||||
package textinput
|
package textinput
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
"runtime"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
@ -73,6 +74,7 @@ var (
|
|||||||
imm32 = windows.NewLazySystemDLL("imm32.dll")
|
imm32 = windows.NewLazySystemDLL("imm32.dll")
|
||||||
user32 = windows.NewLazySystemDLL("user32.dll")
|
user32 = windows.NewLazySystemDLL("user32.dll")
|
||||||
|
|
||||||
|
procImmAssociateContext = imm32.NewProc("ImmAssociateContext")
|
||||||
procImmGetCompositionStringW = imm32.NewProc("ImmGetCompositionStringW")
|
procImmGetCompositionStringW = imm32.NewProc("ImmGetCompositionStringW")
|
||||||
procImmGetContext = imm32.NewProc("ImmGetContext")
|
procImmGetContext = imm32.NewProc("ImmGetContext")
|
||||||
procImmReleaseContext = imm32.NewProc("ImmReleaseContext")
|
procImmReleaseContext = imm32.NewProc("ImmReleaseContext")
|
||||||
@ -94,6 +96,14 @@ func _GetActiveWindow() windows.HWND {
|
|||||||
return windows.HWND(r)
|
return windows.HWND(r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func _ImmAssociateContext(hwnd windows.HWND, hIMC uintptr) (uintptr, error) {
|
||||||
|
r, _, e := procImmAssociateContext.Call(uintptr(hwnd), hIMC)
|
||||||
|
if e != nil && !errors.Is(e, windows.ERROR_SUCCESS) {
|
||||||
|
return 0, fmt.Errorf("textinput: ImmAssociateContext failed: error code: %w", e)
|
||||||
|
}
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
func _ImmGetCompositionStringW(unnamedParam1 _HIMC, unnamedParam2 uint32, lpBuf unsafe.Pointer, dwBufLen uint32) (uint32, error) {
|
func _ImmGetCompositionStringW(unnamedParam1 _HIMC, unnamedParam2 uint32, lpBuf unsafe.Pointer, dwBufLen uint32) (uint32, error) {
|
||||||
r, _, e := procImmGetCompositionStringW.Call(uintptr(unnamedParam1), uintptr(unnamedParam2), uintptr(lpBuf), uintptr(dwBufLen))
|
r, _, e := procImmGetCompositionStringW.Call(uintptr(unnamedParam1), uintptr(unnamedParam2), uintptr(lpBuf), uintptr(dwBufLen))
|
||||||
runtime.KeepAlive(lpBuf)
|
runtime.KeepAlive(lpBuf)
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
package textinput
|
package textinput
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sync"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
@ -29,8 +30,13 @@ type textInput struct {
|
|||||||
origWndProc uintptr
|
origWndProc uintptr
|
||||||
wndProcCallback uintptr
|
wndProcCallback uintptr
|
||||||
window windows.HWND
|
window windows.HWND
|
||||||
|
immContext uintptr
|
||||||
|
|
||||||
highSurrogate uint16
|
highSurrogate uint16
|
||||||
|
|
||||||
|
initOnce sync.Once
|
||||||
|
|
||||||
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
var theTextInput textInput
|
var theTextInput textInput
|
||||||
@ -52,11 +58,29 @@ func (t *textInput) Start(x, y int) (chan State, func()) {
|
|||||||
session.ch <- State{Error: err}
|
session.ch <- State{Error: err}
|
||||||
session.end()
|
session.end()
|
||||||
}
|
}
|
||||||
return session.ch, session.end
|
return session.ch, func() {
|
||||||
|
ui.Get().RunOnMainThread(func() {
|
||||||
|
// Disable IME again.
|
||||||
|
if t.immContext != 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c, err := _ImmAssociateContext(t.window, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.immContext = c
|
||||||
|
t.end()
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// start must be called from the main thread.
|
// start must be called from the main thread.
|
||||||
func (t *textInput) start(x, y int) error {
|
func (t *textInput) start(x, y int) error {
|
||||||
|
if t.err != nil {
|
||||||
|
return t.err
|
||||||
|
}
|
||||||
|
|
||||||
if t.window == 0 {
|
if t.window == 0 {
|
||||||
t.window = _GetActiveWindow()
|
t.window = _GetActiveWindow()
|
||||||
}
|
}
|
||||||
@ -72,6 +96,22 @@ func (t *textInput) start(x, y int) error {
|
|||||||
t.origWndProc = h
|
t.origWndProc = h
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// By default, IME was disabled by setting 0 as the IMM context.
|
||||||
|
// Restore the context once.
|
||||||
|
var err error
|
||||||
|
t.initOnce.Do(func() {
|
||||||
|
err = ui.Get().RestoreIMMContextOnMainThread()
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.immContext != 0 {
|
||||||
|
if _, err := _ImmAssociateContext(t.window, t.immContext); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.immContext = 0
|
||||||
|
}
|
||||||
h := _ImmGetContext(t.window)
|
h := _ImmGetContext(t.window)
|
||||||
if err := _ImmSetCandidateWindow(h, &_CANDIDATEFORM{
|
if err := _ImmSetCandidateWindow(h, &_CANDIDATEFORM{
|
||||||
dwIndex: 0,
|
dwIndex: 0,
|
||||||
@ -168,14 +208,20 @@ func (t *textInput) send(text string, startInBytes, endInBytes int, committed bo
|
|||||||
|
|
||||||
// end must be called from the main thread.
|
// end must be called from the main thread.
|
||||||
func (t *textInput) end() {
|
func (t *textInput) end() {
|
||||||
if t.session != nil {
|
if t.session == nil {
|
||||||
t.session.end()
|
return
|
||||||
t.session = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t.session.end()
|
||||||
|
t.session = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// update must be called from the main thread.
|
// update must be called from the main thread.
|
||||||
func (t *textInput) update() (ferr error) {
|
func (t *textInput) update() (ferr error) {
|
||||||
|
if t.err != nil {
|
||||||
|
return t.err
|
||||||
|
}
|
||||||
|
|
||||||
hIMC := _ImmGetContext(t.window)
|
hIMC := _ImmGetContext(t.window)
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := _ImmReleaseContext(t.window, hIMC); err != nil && ferr != nil {
|
if err := _ImmReleaseContext(t.window, hIMC); err != nil && ferr != nil {
|
||||||
@ -236,6 +282,10 @@ func (t *textInput) update() (ferr error) {
|
|||||||
|
|
||||||
// commit must be called from the main thread.
|
// commit must be called from the main thread.
|
||||||
func (t *textInput) commit() (ferr error) {
|
func (t *textInput) commit() (ferr error) {
|
||||||
|
if t.err != nil {
|
||||||
|
return t.err
|
||||||
|
}
|
||||||
|
|
||||||
hIMC := _ImmGetContext(t.window)
|
hIMC := _ImmGetContext(t.window)
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := _ImmReleaseContext(t.window, hIMC); err != nil && ferr != nil {
|
if err := _ImmReleaseContext(t.window, hIMC); err != nil && ferr != nil {
|
||||||
|
@ -68,9 +68,12 @@ type _POINT struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
imm32 = windows.NewLazySystemDLL("imm32.dll")
|
||||||
ole32 = windows.NewLazySystemDLL("ole32.dll")
|
ole32 = windows.NewLazySystemDLL("ole32.dll")
|
||||||
user32 = windows.NewLazySystemDLL("user32.dll")
|
user32 = windows.NewLazySystemDLL("user32.dll")
|
||||||
|
|
||||||
|
procImmAssociateContext = imm32.NewProc("ImmAssociateContext")
|
||||||
|
|
||||||
procCoCreateInstance = ole32.NewProc("CoCreateInstance")
|
procCoCreateInstance = ole32.NewProc("CoCreateInstance")
|
||||||
|
|
||||||
procGetSystemMetrics = user32.NewProc("GetSystemMetrics")
|
procGetSystemMetrics = user32.NewProc("GetSystemMetrics")
|
||||||
@ -79,6 +82,14 @@ var (
|
|||||||
procGetCursorPos = user32.NewProc("GetCursorPos")
|
procGetCursorPos = user32.NewProc("GetCursorPos")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func _ImmAssociateContext(hwnd windows.HWND, hIMC uintptr) (uintptr, error) {
|
||||||
|
r, _, e := procImmAssociateContext.Call(uintptr(hwnd), hIMC)
|
||||||
|
if e != nil && !errors.Is(e, windows.ERROR_SUCCESS) {
|
||||||
|
return 0, fmt.Errorf("ui: ImmAssociateContext failed: error code: %w", e)
|
||||||
|
}
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
func _CoCreateInstance(rclsid *windows.GUID, pUnkOuter unsafe.Pointer, dwClsContext uint32, riid *windows.GUID) (unsafe.Pointer, error) {
|
func _CoCreateInstance(rclsid *windows.GUID, pUnkOuter unsafe.Pointer, dwClsContext uint32, riid *windows.GUID) (unsafe.Pointer, error) {
|
||||||
var ptr unsafe.Pointer
|
var ptr unsafe.Pointer
|
||||||
r, _, _ := procCoCreateInstance.Call(uintptr(unsafe.Pointer(rclsid)), uintptr(pUnkOuter), uintptr(dwClsContext), uintptr(unsafe.Pointer(riid)), uintptr(unsafe.Pointer(&ptr)))
|
r, _, _ := procCoCreateInstance.Call(uintptr(unsafe.Pointer(rclsid)), uintptr(pUnkOuter), uintptr(dwClsContext), uintptr(unsafe.Pointer(riid)), uintptr(unsafe.Pointer(&ptr)))
|
||||||
|
@ -444,3 +444,7 @@ func (u *UserInterface) setDocumentEdited(edited bool) error {
|
|||||||
objc.ID(w).Send(sel_setDocumentEdited, edited)
|
objc.ID(w).Send(sel_setDocumentEdited, edited)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *UserInterface) afterWindowCreation() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -106,6 +106,9 @@ type userInterfaceImpl struct {
|
|||||||
showWindowOnce sync.Once
|
showWindowOnce sync.Once
|
||||||
bufferOnceSwappedOnce sync.Once
|
bufferOnceSwappedOnce sync.Once
|
||||||
|
|
||||||
|
// immContext is used only in Windows.
|
||||||
|
immContext uintptr
|
||||||
|
|
||||||
m sync.RWMutex
|
m sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -897,6 +900,10 @@ func (u *UserInterface) createWindow() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := u.afterWindowCreation(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,3 +204,7 @@ func (u *UserInterface) skipTaskbar() error {
|
|||||||
func (u *UserInterface) setDocumentEdited(edited bool) error {
|
func (u *UserInterface) setDocumentEdited(edited bool) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *UserInterface) afterWindowCreation() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -246,6 +246,38 @@ func (u *UserInterface) setDocumentEdited(edited bool) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *UserInterface) afterWindowCreation() error {
|
||||||
|
if microsoftgdk.IsXbox() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// By default, IME should be disabled (#2918).
|
||||||
|
w, err := u.window.GetWin32Window()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c, err := _ImmAssociateContext(w, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
u.immContext = c
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RestoreIMMContextOnMainThread is called from the main thread.
|
||||||
|
// The textinput package invokes RestoreIMMContextOnMainThread to enable IME inputting.
|
||||||
|
func (u *UserInterface) RestoreIMMContextOnMainThread() error {
|
||||||
|
w, err := u.window.GetWin32Window()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err := _ImmAssociateContext(w, u.immContext); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
u.immContext = 0
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
if microsoftgdk.IsXbox() {
|
if microsoftgdk.IsXbox() {
|
||||||
// TimeBeginPeriod might not be defined in Xbox.
|
// TimeBeginPeriod might not be defined in Xbox.
|
||||||
|
Loading…
Reference in New Issue
Block a user