From 4c7db129df3f6d56a809826a6fa76375dca422f6 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Fri, 28 Jun 2024 16:14:52 -0700 Subject: [PATCH] SDL_HINT_IME_INTERNAL_EDITING and SDL_HINT_IME_SHOW_UI are replaced with SDL_HINT_IME_NATIVE_UI --- docs/README-migration.md | 2 + include/SDL3/SDL_hints.h | 31 +++----- src/core/linux/SDL_fcitx.c | 17 ++-- src/core/linux/SDL_ibus.c | 18 +++-- src/video/windows/SDL_windowskeyboard.c | 101 +++++++++++++++--------- src/video/windows/SDL_windowsvideo.h | 5 +- test/testime.c | 21 ++++- 7 files changed, 119 insertions(+), 76 deletions(-) diff --git a/docs/README-migration.md b/docs/README-migration.md index 122983b49..15bb9650a 100644 --- a/docs/README-migration.md +++ b/docs/README-migration.md @@ -757,6 +757,8 @@ The following hints have been removed: * SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS - gamepad buttons are always positional * SDL_HINT_GRAB_KEYBOARD - use SDL_SetWindowKeyboardGrab() instead * SDL_HINT_IDLE_TIMER_DISABLED - use SDL_DisableScreenSaver() instead +* SDL_HINT_IME_INTERNAL_EDITING - replaced with SDL_HINT_IME_NATIVE_UI +* SDL_HINT_IME_SHOW_UI - replaced with SDL_HINT_IME_NATIVE_UI * SDL_HINT_IME_SUPPORT_EXTENDED_TEXT - the normal text editing event has extended text * SDL_HINT_MOUSE_RELATIVE_SCALING - mouse coordinates are no longer automatically scaled by the SDL renderer * SDL_HINT_PS2_DYNAMIC_VSYNC - use SDL_SetRenderVSync(renderer, -1) instead diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h index 3a5e02637..07e2ea82f 100644 --- a/include/SDL3/SDL_hints.h +++ b/include/SDL3/SDL_hints.h @@ -943,37 +943,26 @@ extern "C" { #define SDL_HINT_HIDAPI_IGNORE_DEVICES "SDL_HIDAPI_IGNORE_DEVICES" /** - * A variable to control whether certain IMEs should handle text editing - * internally instead of sending SDL_EVENT_TEXT_EDITING events. + * A variable describing what IME elements the OS should render natively over the game. * - * The variable can be set to the following values: + * By default IME UI is handled using native components by the OS, however this interferes with fullscreen games in some cases. * - * - "0": SDL_EVENT_TEXT_EDITING events are sent, and it is the application's - * responsibility to render the text from these events and differentiate it - * somehow from committed text. (default) - * - "1": If supported by the IME then SDL_EVENT_TEXT_EDITING events are not - * sent, and text that is being composed will be rendered in its own UI. + * The variable can be set to a comma separated list containing the following items: * - * This hint can be set anytime. + * - "none" or "0": Native UI elements will not be displayed. + * - "composition": Native UI elements will be used for the IME composition string. + * - "candidates": Native UI elements will be used for the IME candidate list. + * - "all" or "1": Native UI elements will be used for all IME UI. (default) * - * \since This hint is available since SDL 3.0.0. - */ -#define SDL_HINT_IME_INTERNAL_EDITING "SDL_IME_INTERNAL_EDITING" - -/** - * A variable to control whether certain IMEs should show native UI components - * (such as the Candidate List) instead of suppressing them. + * If native UI is used for the composition string, then SDL_EVENT_TEXT_EDITING will not be sent. * - * The variable can be set to the following values: - * - * - "0": Native UI components are not display. - * - "1": Native UI components are displayed. (default) + * If native UI is used for the candidates list, then SDL_EVENT_TEXT_EDITING_CANDIDATES will not be sent. * * This hint should be set before SDL is initialized. * * \since This hint is available since SDL 3.0.0. */ -#define SDL_HINT_IME_SHOW_UI "SDL_IME_SHOW_UI" +#define SDL_HINT_IME_NATIVE_UI "SDL_IME_NATIVE_UI" /** * A variable controlling whether the home indicator bar on iPhone X should be diff --git a/src/core/linux/SDL_fcitx.c b/src/core/linux/SDL_fcitx.c index 41895fa22..44b1bb54d 100644 --- a/src/core/linux/SDL_fcitx.c +++ b/src/core/linux/SDL_fcitx.c @@ -229,7 +229,7 @@ static void FcitxClientICCallMethod(FcitxClient *client, const char *method) static void SDLCALL Fcitx_SetCapabilities(void *data, const char *name, const char *old_val, - const char *internal_editing) + const char *hint) { FcitxClient *client = (FcitxClient *)data; Uint64 caps = 0; @@ -237,9 +237,16 @@ static void SDLCALL Fcitx_SetCapabilities(void *data, return; } - if (!(internal_editing && *internal_editing == '1')) { - caps |= (1 << 1); /* Preedit Flag */ - caps |= (1 << 4); /* Formatted Preedit Flag */ + if (!hint || !*hint || *hint == '1' || SDL_strstr(hint, "all")) { + // Let the OS handle IME UI + } else { + if (!SDL_strstr(hint, "composition")) { + caps |= (1 << 1); /* Preedit Flag */ + caps |= (1 << 4); /* Formatted Preedit Flag */ + } + if (!SDL_strstr(hint, "candidates")) { + // FIXME, turn off native candidate rendering + } } SDL_DBus_CallVoidMethod(FCITX_DBUS_SERVICE, client->ic_path, FCITX_IC_DBUS_INTERFACE, "SetCapability", DBUS_TYPE_UINT64, &caps, DBUS_TYPE_INVALID); @@ -300,7 +307,7 @@ static SDL_bool FcitxClientCreateIC(FcitxClient *client) NULL); dbus->connection_flush(dbus->session_conn); - SDL_AddHintCallback(SDL_HINT_IME_INTERNAL_EDITING, Fcitx_SetCapabilities, client); + SDL_AddHintCallback(SDL_HINT_IME_NATIVE_UI, Fcitx_SetCapabilities, client); return SDL_TRUE; } diff --git a/src/core/linux/SDL_ibus.c b/src/core/linux/SDL_ibus.c index ed73cc5bd..c34ebc96c 100644 --- a/src/core/linux/SDL_ibus.c +++ b/src/core/linux/SDL_ibus.c @@ -404,14 +404,22 @@ static char *IBus_GetDBusAddressFilename(void) static SDL_bool IBus_CheckConnection(SDL_DBusContext *dbus); static void SDLCALL IBus_SetCapabilities(void *data, const char *name, const char *old_val, - const char *internal_editing) + const char *hint) { SDL_DBusContext *dbus = SDL_DBus_GetContext(); if (IBus_CheckConnection(dbus)) { Uint32 caps = IBUS_CAP_FOCUS; - if (!(internal_editing && *internal_editing == '1')) { - caps |= IBUS_CAP_PREEDIT_TEXT; + + if (!hint || !*hint || *hint == '1' || SDL_strstr(hint, "all")) { + // Let the OS handle IME UI + } else { + if (!SDL_strstr(hint, "composition")) { + caps |= IBUS_CAP_PREEDIT_TEXT; + } + if (!SDL_strstr(hint, "candidates")) { + // FIXME, turn off native candidate rendering + } } SDL_DBus_CallVoidMethodOnConnection(ibus_conn, ibus_service, input_ctx_path, ibus_input_interface, "SetCapabilities", @@ -474,7 +482,7 @@ static SDL_bool IBus_SetupConnection(SDL_DBusContext *dbus, const char *addr) (void)SDL_snprintf(matchstr, sizeof(matchstr), "type='signal',interface='%s'", ibus_input_interface); SDL_free(input_ctx_path); input_ctx_path = SDL_strdup(path); - SDL_AddHintCallback(SDL_HINT_IME_INTERNAL_EDITING, IBus_SetCapabilities, NULL); + SDL_AddHintCallback(SDL_HINT_IME_NATIVE_UI, IBus_SetCapabilities, NULL); dbus->bus_add_match(ibus_conn, matchstr, NULL); dbus->connection_try_register_object_path(ibus_conn, input_ctx_path, &ibus_vtable, dbus, NULL); dbus->connection_flush(ibus_conn); @@ -631,7 +639,7 @@ void SDL_IBus_Quit(void) /* !!! FIXME: should we close(inotify_fd) here? */ - SDL_DelHintCallback(SDL_HINT_IME_INTERNAL_EDITING, IBus_SetCapabilities, NULL); + SDL_DelHintCallback(SDL_HINT_IME_NATIVE_UI, IBus_SetCapabilities, NULL); SDL_memset(&ibus_cursor_rect, 0, sizeof(ibus_cursor_rect)); } diff --git a/src/video/windows/SDL_windowskeyboard.c b/src/video/windows/SDL_windowskeyboard.c index 95d6c3ea1..d654699bf 100644 --- a/src/video/windows/SDL_windowskeyboard.c +++ b/src/video/windows/SDL_windowskeyboard.c @@ -306,11 +306,6 @@ static DWORD IME_GetId(SDL_VideoData *videodata, UINT uIndex); static void IME_SendEditingEvent(SDL_VideoData *videodata); static void IME_SendClearComposition(SDL_VideoData *videodata); -static SDL_bool WIN_ShouldShowNativeUI() -{ - return SDL_GetHintBoolean(SDL_HINT_IME_SHOW_UI, SDL_TRUE); -} - static int IME_Init(SDL_VideoData *videodata, SDL_Window *window) { HWND hwnd = window->driverdata->hwnd; @@ -319,6 +314,19 @@ static int IME_Init(SDL_VideoData *videodata, SDL_Window *window) return 0; } + const char *hint = SDL_GetHint(SDL_HINT_IME_NATIVE_UI); + if (!hint || !*hint || *hint == '1' || SDL_strstr(hint, "all")) { + videodata->ime_native_composition = SDL_TRUE; + videodata->ime_native_candidates = SDL_TRUE; + } else { + if (SDL_strstr(hint, "composition")) { + videodata->ime_native_composition = SDL_TRUE; + } + if (SDL_strstr(hint, "candidates")) { + videodata->ime_native_candidates = SDL_TRUE; + } + } + videodata->ime_hwnd_main = hwnd; videodata->ime_initialized = SDL_TRUE; videodata->ime_himm32 = SDL_LoadObject("imm32.dll"); @@ -345,11 +353,6 @@ static int IME_Init(SDL_VideoData *videodata, SDL_Window *window) videodata->ime_available = SDL_TRUE; IME_UpdateInputLocale(videodata); IME_SetupAPI(videodata); - if (WIN_ShouldShowNativeUI()) { - videodata->ime_uiless = SDL_FALSE; - } else { - videodata->ime_uiless = SDL_TRUE; - } IME_UpdateInputLocale(videodata); IME_Disable(videodata, hwnd); return 0; @@ -541,7 +544,8 @@ static DWORD IME_GetId(SDL_VideoData *videodata, UINT uIndex) SDL_assert(uIndex == 0); dwLang = ((DWORD_PTR)hkl & 0xffff); - if (videodata->ime_uiless && dwLang == LANG_CHT) { + // FIXME: What does this do? + if (!videodata->ime_native_candidates && dwLang == LANG_CHT) { dwRet[0] = IMEID_CHT_VER_VISTA; dwRet[1] = 0; return dwRet[0]; @@ -972,9 +976,21 @@ SDL_bool WIN_HandleIMEMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam if (msg == WM_IME_SETCONTEXT) { SDL_DebugIMELog("WM_IME_SETCONTEXT\n"); - if (videodata->ime_uiless) { - *lParam = 0; + + LPARAM element_mask; + if (!videodata->ime_native_composition && !videodata->ime_native_candidates) { + element_mask = 0; + } else { + element_mask = ISC_SHOWUIALL; + if (!videodata->ime_native_composition) { + element_mask &= ~ISC_SHOWUICOMPOSITIONWINDOW; + } + if (!videodata->ime_native_candidates) { + element_mask &= ~ISC_SHOWUIALLCANDIDATEWINDOW; + } } + *lParam &= element_mask; + return SDL_FALSE; } @@ -997,34 +1013,41 @@ SDL_bool WIN_HandleIMEMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam break; case WM_IME_STARTCOMPOSITION: SDL_DebugIMELog("WM_IME_STARTCOMPOSITION\n"); - trap = SDL_TRUE; + if (!videodata->ime_native_composition) { + trap = SDL_TRUE; + } break; case WM_IME_COMPOSITION: SDL_DebugIMELog("WM_IME_COMPOSITION %x\n", lParam); - trap = SDL_TRUE; - himc = ImmGetContext(hwnd); - if (*lParam & GCS_RESULTSTR) { - SDL_DebugIMELog("GCS_RESULTSTR\n"); - IME_GetCompositionString(videodata, himc, GCS_RESULTSTR); - IME_SendClearComposition(videodata); - IME_SendInputEvent(videodata); + if (!videodata->ime_native_composition) { + trap = SDL_TRUE; + himc = ImmGetContext(hwnd); + if (*lParam & GCS_RESULTSTR) { + SDL_DebugIMELog("GCS_RESULTSTR\n"); + IME_GetCompositionString(videodata, himc, GCS_RESULTSTR); + IME_SendClearComposition(videodata); + IME_SendInputEvent(videodata); + } + if (*lParam & GCS_COMPSTR) { + SDL_DebugIMELog("GCS_COMPSTR\n"); + videodata->ime_readingstring[0] = 0; + IME_GetCompositionString(videodata, himc, GCS_COMPSTR); + IME_SendEditingEvent(videodata); + } + ImmReleaseContext(hwnd, himc); } - if (*lParam & GCS_COMPSTR) { - SDL_DebugIMELog("GCS_COMPSTR\n"); - videodata->ime_readingstring[0] = 0; - IME_GetCompositionString(videodata, himc, GCS_COMPSTR); - IME_SendEditingEvent(videodata); - } - ImmReleaseContext(hwnd, himc); break; case WM_IME_ENDCOMPOSITION: SDL_DebugIMELog("WM_IME_ENDCOMPOSITION\n"); - videodata->ime_composition[0] = 0; - videodata->ime_readingstring[0] = 0; - videodata->ime_cursor = 0; - videodata->ime_selected_start = 0; - videodata->ime_selected_length = 0; - IME_SendClearComposition(videodata); + if (!videodata->ime_native_composition) { + trap = SDL_TRUE; + videodata->ime_composition[0] = 0; + videodata->ime_readingstring[0] = 0; + videodata->ime_cursor = 0; + videodata->ime_selected_start = 0; + videodata->ime_selected_length = 0; + IME_SendClearComposition(videodata); + } break; case WM_IME_NOTIFY: SDL_DebugIMELog("WM_IME_NOTIFY %x\n", wParam); @@ -1043,24 +1066,24 @@ SDL_bool WIN_HandleIMEMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM *lParam case IMN_OPENCANDIDATE: case IMN_CHANGECANDIDATE: SDL_DebugIMELog("%s\n", wParam == IMN_OPENCANDIDATE ? "IMN_OPENCANDIDATE" : "IMN_CHANGECANDIDATE"); - if (videodata->ime_uiless) { - videodata->ime_update_candidates = SDL_TRUE; + if (!videodata->ime_native_candidates) { trap = SDL_TRUE; + videodata->ime_update_candidates = SDL_TRUE; } break; case IMN_CLOSECANDIDATE: SDL_DebugIMELog("IMN_CLOSECANDIDATE\n"); - if (videodata->ime_uiless) { + if (!videodata->ime_native_candidates) { + trap = SDL_TRUE; videodata->ime_update_candidates = SDL_FALSE; IME_CloseCandidateList(videodata); - trap = SDL_TRUE; } break; case IMN_PRIVATE: { DWORD dwId = IME_GetId(videodata, 0); - IME_GetReadingString(videodata, hwnd); SDL_DebugIMELog("IMN_PRIVATE %u\n", dwId); + IME_GetReadingString(videodata, hwnd); switch (dwId) { case IMEID_CHT_VER42: case IMEID_CHT_VER43: diff --git a/src/video/windows/SDL_windowsvideo.h b/src/video/windows/SDL_windowsvideo.h index c2cf4677a..7f74a2777 100644 --- a/src/video/windows/SDL_windowsvideo.h +++ b/src/video/windows/SDL_windowsvideo.h @@ -339,7 +339,6 @@ typedef struct void *data; } TSFSink; -#ifndef SDL_DISABLE_WINDOWS_IME /* Definition from Win98DDK version of IMM.H */ typedef struct tagINPUTCONTEXT2 { @@ -365,7 +364,6 @@ typedef struct tagINPUTCONTEXT2 DWORD fdwInit; DWORD dwReserve[3]; } INPUTCONTEXT2, *PINPUTCONTEXT2, NEAR *NPINPUTCONTEXT2, FAR *LPINPUTCONTEXT2; -#endif /* !SDL_DISABLE_WINDOWS_IME */ /* Private display data */ @@ -461,7 +459,8 @@ struct SDL_VideoData BOOL (WINAPI *ImmUnlockIMCC)(HIMCC himcc); /* *INDENT-ON* */ /* clang-format on */ - SDL_bool ime_uiless; + SDL_bool ime_native_composition; + SDL_bool ime_native_candidates; #endif /* !SDL_DISABLE_WINDOWS_IME */ BYTE pre_hook_key_state[256]; diff --git a/test/testime.c b/test/testime.c index 24d300653..1ed6e0cb9 100644 --- a/test/testime.c +++ b/test/testime.c @@ -867,6 +867,8 @@ static void Redraw(void) int main(int argc, char *argv[]) { + SDL_bool native_composition = SDL_TRUE; + SDL_bool native_candidates = SDL_TRUE; int i, done; SDL_Event event; char *fontname = NULL; @@ -890,12 +892,15 @@ int main(int argc, char *argv[]) fontname = argv[i + 1]; consumed = 2; } - } else if (SDL_strcmp(argv[i], "--disable-ui") == 0) { - SDL_SetHint(SDL_HINT_IME_SHOW_UI, "0"); + } else if (SDL_strcmp(argv[i], "--disable-native-composition") == 0) { + native_composition = SDL_FALSE; + consumed = 1; + } else if (SDL_strcmp(argv[i], "--disable-native-candidates") == 0) { + native_candidates = SDL_FALSE; consumed = 1; } if (consumed <= 0) { - static const char *options[] = { "[--font fontfile] [--disable-ui]", NULL }; + static const char *options[] = { "[--font fontfile] [--disable-native-composition] [--disable-native-candidates]", NULL }; SDLTest_CommonLogUsage(state, argv[0], options); return 1; } @@ -903,6 +908,16 @@ int main(int argc, char *argv[]) i += consumed; } + if (native_composition && native_candidates) { + SDL_SetHint(SDL_HINT_IME_NATIVE_UI, "1"); + } else if (native_composition) { + SDL_SetHint(SDL_HINT_IME_NATIVE_UI, "composition"); + } else if (native_candidates) { + SDL_SetHint(SDL_HINT_IME_NATIVE_UI, "candidates"); + } else { + SDL_SetHint(SDL_HINT_IME_NATIVE_UI, "0"); + } + if (!SDLTest_CommonInit(state)) { return 2; }