diff --git a/CHANGELOG b/CHANGELOG index 0f0231555..dfd6c05ae 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -76,6 +76,10 @@ config option "getsysfont" is added which when set to "false" will disable the font acquisition on Windows and Linux platforms. (Wengier & nanshiki) + - Implemented support for inputing CJK characters + on Linux SDL1 builds using Linux system IMEs. Code + imported from SDL-IM-plus by nanshiki and confirmed + to work on Linux/X11 platform by him. - Added support for inputing Japanese and Korean characters using system input method (IME) using the default "windib" video driver in Windows SDL1 diff --git a/src/dosbox.cpp b/src/dosbox.cpp index 1ab3cf28f..6a6d79b96 100644 --- a/src/dosbox.cpp +++ b/src/dosbox.cpp @@ -1056,11 +1056,13 @@ void DOSBOX_RealInit() { } gbk = dosv_section->Get_bool("gbk"); dos.loaded_codepage = cp; -#if defined(WIN32) && !defined(HX_DOS) && !defined(C_SDL2) && defined(SDL_DOSBOX_X_SPECIAL) +#if (defined(WIN32) && !defined(HX_DOS) || defined(LINUX) && C_X11) && !defined(C_SDL2) && defined(SDL_DOSBOX_X_SPECIAL) if (enableime && !control->opt_silent) { dos.im_enable_flag = true; SDL_SetIMValues(SDL_IM_ENABLE, 1, NULL); +#if defined(WIN32) SDL_EnableUNICODE(1); +#endif } else if (!control->opt_silent) { dos.im_enable_flag = false; SDL_SetIMValues(SDL_IM_ENABLE, 0, NULL); @@ -2026,16 +2028,17 @@ void DOSBOX_SetupConfigSections(void) { Pstring = secprop->Add_string("dosv",Property::Changeable::WhenIdle,"off"); Pstring->Set_values(dosv_settings); - Pstring->Set_help("Enable DOS/V emulation and specify which version to emulate. This option is intended for\n" - "use with games or software originating from East Asia that use the double byte character set (DBCS)\n" - "encodings and DOS/V extensions to display Japanese (jp), Chinese (chs/cht/cn/tw), or Korean (ko) text.\n" - "Note that enabling DOS/V replaces 80x25 text mode (INT 10h mode 3) with a EGA/VGA graphics\n" - "mode that emulates text mode to display the characters and may be incompatible with non-Asian\n" - "software that assumes direct access to the text mode via segment 0xB800."); + Pstring->Set_help("Enable DOS/V emulation and specify which version to emulate. This option is intended for use with games or software\n" + "originating from East Asia (China, Japan, Korea) that use the double byte character set (DBCS) encodings and DOS/V extensions\n" + "to display Japanese (jp), Chinese (chs/cht/cn/tw), or Korean (ko) text. Note that enabling DOS/V replaces 80x25 text mode with\n" + "a EGA/VGA graphics mode that emulates text mode to display the characters and may be incompatible with non-Asian software that\n" + "assumes direct access to the text mode via segment 0xB800. For a general DOS environment with CJK support please disable DOS/V\n" + "emulation and use TrueType font (TTF) output with a CJK code page (932, 936, 949, 950) and TTF font with CJK characters instead."); Pstring->SetBasic(true); Pbool = secprop->Add_bool("getsysfont",Property::Changeable::OnlyAtStart,true); - Pbool->Set_help("If enabled, DOSBox-X will try to get and use the system fonts on Windows and Linux platforms for the DOS/V emulation."); + Pbool->Set_help("If enabled, DOSBox-X will try to get and use the system fonts on Windows and Linux platforms for the DOS/V emulation.\n" + "If this cannot be done, then DOSBox-X will try to use the internal Japanese DOS/V font, or you can specify a different font."); Pbool->SetBasic(true); //For loading FONTX CJK fonts diff --git a/src/gui/sdlmain.cpp b/src/gui/sdlmain.cpp index 3bb64d78e..2cef4a6b8 100644 --- a/src/gui/sdlmain.cpp +++ b/src/gui/sdlmain.cpp @@ -7375,7 +7375,7 @@ void* GetSetSDLValue(int isget, std::string& target, void* setval) { return NULL; } -#if defined(WIN32) && !defined(HX_DOS) && !defined(C_SDL2) && defined(SDL_DOSBOX_X_SPECIAL) +#if (defined(WIN32) && !defined(HX_DOS) || defined(LINUX) && C_X11) && !defined(C_SDL2) && defined(SDL_DOSBOX_X_SPECIAL) static Bitu im_x, im_y; static uint32_t last_ticks; void SetIMPosition() { @@ -7397,8 +7397,8 @@ void SetIMPosition() { last_ticks = GetTicks(); im_x = x; im_y = y; -#if defined(LINUX) - y++; +#if DOSBOXMENU_TYPE == DOSBOXMENU_SDLDRAW + y+=mainMenu.menuBarHeightBase; #endif uint8_t height = IS_PC98_ARCH?16:real_readb(BIOSMEM_SEG, BIOSMEM_CHAR_HEIGHT); uint8_t width = CurMode && DOSV_CheckCJKVideoMode() ? CurMode->cwidth : (height / 2); @@ -8153,6 +8153,27 @@ void GFX_Events() { if (event.key.keysym.sym==SDLK_RCTRL) sdl.rctrlstate = event.key.type; if (event.key.keysym.sym==SDLK_LSHIFT) sdl.lshiftstate = event.key.type; if (event.key.keysym.sym==SDLK_RSHIFT) sdl.rshiftstate = event.key.type; +#if defined(LINUX) && C_X11 + if (event.type == SDL_KEYDOWN) { + if((IS_PC98_ARCH || isDBCSCP()) && event.key.keysym.unicode != 0) { + SetIMPosition(); + char chars[10]; + uint16_t uname[2]; + uname[0]=event.key.keysym.unicode; + uname[1]=0; + if (CodePageHostToGuestUTF16(chars, uname)) { + for (size_t i=0; iopt_silent) { SDL_SetIMValues(SDL_IM_ONOFF, 0, NULL); SDL_SetIMValues(SDL_IM_ENABLE, 0, NULL); diff --git a/src/ints/int_dosv.cpp b/src/ints/int_dosv.cpp index c53ae332b..01d23d25d 100644 --- a/src/ints/int_dosv.cpp +++ b/src/ints/int_dosv.cpp @@ -776,7 +776,7 @@ void JFONT_Init() { fontdata16 = NULL; fontsize16 = 0; } -#if defined(WIN32) && !defined(HX_DOS) && !defined(C_SDL2) && defined(SDL_DOSBOX_X_SPECIAL) +#if (defined(WIN32) && !defined(HX_DOS) || defined(LINUX) && C_X11) && !defined(C_SDL2) && defined(SDL_DOSBOX_X_SPECIAL) SDL_SetCompositionFontName(jfont_name); #endif Section_prop *section = static_cast(control->GetSection("dosv")); @@ -998,7 +998,7 @@ static Bitu mskanji_api(void) real_writeb(kk_seg, kk_off + 5, 0); reg_ax = 0; } else if(func == 5) { -#if defined(WIN32) && !defined(HX_DOS) && !defined(C_SDL2) && defined(SDL_DOSBOX_X_SPECIAL) +#if (defined(WIN32) && !defined(HX_DOS) || defined(LINUX) && C_X11) && !defined(C_SDL2) && defined(SDL_DOSBOX_X_SPECIAL) if(mode & 0x8000) { if(mode & 0x0001) SDL_SetIMValues(SDL_IM_ONOFF, 0, NULL); @@ -1349,7 +1349,7 @@ uint8_t GetKanjiAttr() void INT8_DOSV() { -#if defined(WIN32) && !defined(HX_DOS) && !defined(C_SDL2) && defined(SDL_DOSBOX_X_SPECIAL) +#if (defined(WIN32) && !defined(HX_DOS) || defined(LINUX) && C_X11) && !defined(C_SDL2) && defined(SDL_DOSBOX_X_SPECIAL) SetIMPosition(); #endif if(!CheckAnotherDisplayDriver() && real_readb(BIOSMEM_SEG,BIOSMEM_CURRENT_MODE) != 0x72) { diff --git a/vs2015/sdl/include/SDL_platform.h b/vs2015/sdl/include/SDL_platform.h index 87b3620fe..f40f4db46 100644 --- a/vs2015/sdl/include/SDL_platform.h +++ b/vs2015/sdl/include/SDL_platform.h @@ -66,6 +66,7 @@ #if defined(linux) || defined(__linux) || defined(__linux__) #undef __LINUX__ #define __LINUX__ 1 +#define ENABLE_IM_EVENT 1 #endif #if defined(__APPLE__) #undef __MACOSX__ diff --git a/vs2015/sdl/src/video/x11/SDL_x11events.c b/vs2015/sdl/src/video/x11/SDL_x11events.c index 923f9e58a..0dd5a144a 100644 --- a/vs2015/sdl/src/video/x11/SDL_x11events.c +++ b/vs2015/sdl/src/video/x11/SDL_x11events.c @@ -58,6 +58,14 @@ static SDLKey ODD_keymap[256]; static SDLKey MISC_keymap[256]; SDLKey X11_TranslateKeycode(Display *display, KeyCode kc); +#ifdef ENABLE_IM_EVENT + +void xim_lookup_key(_THIS, XKeyPressedEvent *event); +/* Buffer size required by xim_lookup_key */ +#define BASE_BUFSIZE 50 + +#endif /* ENABLE_IM_EVENT */ + /* Pending resize target for ConfigureNotify (so outdated events don't cause inappropriate resize events) @@ -402,6 +410,14 @@ static int X11_DispatchEvent(_THIS) SDL_memset(&xevent, '\0', sizeof (XEvent)); /* valgrind fix. --ryan. */ XNextEvent(SDL_Display, &xevent); +#ifdef ENABLE_IM_EVENT + if (IM_Context.im_enable && IM_Context.ic_focus) { + if (XFilterEvent(&xevent, None)) { + return 0; + } + } +#endif + /* Discard KeyRelease and KeyPress events generated by auto-repeat. We need to do it before passing event to XFilterEvent. Otherwise, KeyRelease aware IMs are confused... */ @@ -482,6 +498,13 @@ printf("FocusIn!\n"); /* Queue entry into fullscreen mode */ switch_waiting = 0x01 | SDL_FULLSCREEN; switch_time = SDL_GetTicks() + 1500; + +#ifdef ENABLE_IM_EVENT + if (IM_Context.SDL_XIC && IM_Context.im_enable) { + XSetICFocus(IM_Context.SDL_XIC); + IM_Context.ic_focus = 1; + } +#endif } } break; @@ -506,6 +529,13 @@ printf("FocusOut!\n"); /* Queue leaving fullscreen mode */ switch_waiting = 0x01; switch_time = SDL_GetTicks() + 200; + +#ifdef ENABLE_IM_EVENT + if (IM_Context.SDL_XIC && IM_Context.im_enable) { + XUnsetICFocus(IM_Context.SDL_XIC); + IM_Context.ic_focus = 0; + } +#endif } } break; @@ -575,6 +605,12 @@ printf("KeymapNotify!\n"); #ifdef DEBUG_XEVENTS printf("KeyPress (X11 keycode = 0x%X)\n", xevent.xkey.keycode); +#endif + +#ifdef ENABLE_IM_EVENT + if (IM_Context.im_enable && IM_Context.ic_focus) { + xim_lookup_key(this, (XKeyPressedEvent *)&xevent); + } #endif /* If we're not doing translation, we're done! */ if ( !SDL_TranslateUNICODE ) { @@ -1460,3 +1496,358 @@ void X11_InitOSKeymap(_THIS) X11_InitKeymap(); } +#ifdef ENABLE_IM_EVENT +extern int SDL_TranslateUNICODE; +void xim_lookup_key(_THIS, XKeyPressedEvent *event) +{ + KeySym keysym; + Status status; + int i; + + if (IM_Context.SDL_XIC) { + if (!IM_Context.string.im_wide_char_buffer) { + IM_Context.im_buffer_len = BASE_BUFSIZE; + IM_Context.string.im_wide_char_buffer = (wchar_t*)malloc(IM_Context.im_buffer_len*sizeof(wchar_t)); + } + + //memset(IM_Context.string.im_wide_char_buffer, 0, IM_Context.im_buffer_len*sizeof(wchar_t)); + IM_Context.string.im_wide_char_buffer[0] = '\0'; + + //if (SDL_TranslateUNICODE) + IM_Context.im_compose_len = XwcLookupString(IM_Context.SDL_XIC, event, IM_Context.string.im_wide_char_buffer, IM_Context.im_buffer_len, &keysym, &status); + //else + // IM_Context.im_compose_len = XmbLookupString(IM_Context.SDL_XIC, event, IM_Context.string.im_multi_byte_buffer, IM_Context.im_buffer_len*sizeof(wchar_t), &keysym, &status); + + if ((status == XBufferOverflow)) { + IM_Context.im_buffer_len = IM_Context.im_compose_len + 1; + IM_Context.string.im_wide_char_buffer = (wchar_t*)realloc(IM_Context.string.im_wide_char_buffer, IM_Context.im_buffer_len*sizeof(wchar_t)); + //memset(IM_Context.string.im_wide_char_buffer, 0, IM_Context.im_buffer_len*sizeof(wchar_t)); + + //if (SDL_TranslateUNICODE) + IM_Context.im_compose_len = XwcLookupString(IM_Context.SDL_XIC, event, IM_Context.string.im_wide_char_buffer, IM_Context.im_buffer_len, &keysym, &status); + //else + // IM_Context.im_compose_len = XmbLookupString(IM_Context.SDL_XIC, event, IM_Context.string.im_multi_byte_buffer, IM_Context.im_buffer_len*sizeof(wchar_t), &keysym, &status); + } + if (status != XLookupChars) { + IM_Context.im_compose_len = 0; + } + else { + //if (SDL_TranslateUNICODE) + IM_Context.string.im_wide_char_buffer[IM_Context.im_compose_len] = '\0'; + //else + // IM_Context.string.im_multi_byte_buffer[IM_Context.im_compose_len] = '\0'; + } + for (i = 0; i < IM_Context.im_compose_len ; i++) { + SDL_keysym sdlkeysym; + sdlkeysym.scancode = 0; + sdlkeysym.sym = SDLK_UNKNOWN; + sdlkeysym.mod = KMOD_NONE; + sdlkeysym.unicode = IM_Context.string.im_wide_char_buffer[i]; + SDL_PrivateKeyboard(SDL_PRESSED, &sdlkeysym); + } + } + else { + IM_Context.im_compose_len = 0; + SDL_SetError("SDL_XIC is NULL."); + } +} + +int X11_SetIMPosition(_THIS, int x, int y) +{ + XPoint spot; + XVaNestedList preedit_attr; + + if (!IM_Context.SDL_XIC) { + SDL_SetError("SDL_XIC is NULL."); + return 0; + } + + spot.x = x; + spot.y = y; + + preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &spot, NULL); + XSetICValues(IM_Context.SDL_XIC, XNPreeditAttributes, preedit_attr, NULL); + XFree(preedit_attr); + + return 1; +} + +char *X11_SetIMValues(_THIS, SDL_imvalue value, int alt) +{ + if (IM_Context.SDL_XIC) { + switch (value) { + case SDL_IM_ENABLE: + if (alt) { + if (!IM_Context.im_enable) { + XSetICFocus(IM_Context.SDL_XIC); + IM_Context.ic_focus = 1; + IM_Context.im_enable = 1; + } + return NULL; + } + else { + if (IM_Context.im_enable) { + XUnsetICFocus(IM_Context.SDL_XIC); + IM_Context.im_enable = 0; + IM_Context.ic_focus = 0; + } + return NULL; + } + + case SDL_IM_FLIP: + /* backup original attributes */ + if (IM_Context.im_style_orig == 0) { + XGetICValues(IM_Context.SDL_XIC, + XNInputStyle, + &IM_Context.im_style_orig, NULL); + if (IM_Context.im_style_orig == 0) { + IM_Context.im_style_orig = IM_Context.im_style_now; + } + } + if (IM_Context.preedit_attr_orig == NULL) { + XGetICValues(IM_Context.SDL_XIC, + XNPreeditAttributes, + &IM_Context.preedit_attr_orig, NULL); + if (IM_Context.preedit_attr_orig == NULL) { + IM_Context.preedit_attr_orig = IM_Context.preedit_attr_now; + } + } + if (IM_Context.status_attr_orig == NULL) { + XGetICValues(IM_Context.SDL_XIC, + XNStatusAttributes, + &IM_Context.status_attr_orig, NULL); + if (IM_Context.status_attr_orig == NULL) { + IM_Context.status_attr_orig = IM_Context.status_attr_now; + } + } + + if (alt) { + /* Back to prev style mode, exclude OnTheSpot mode. */ + + /* If the now style is OnTheSpot */ + XGetICValues(IM_Context.SDL_XIC, XNInputStyle, + &IM_Context.im_style_now, NULL); + + if (IM_Context.im_style_now & + (XIMPreeditCallbacks | XIMStatusCallbacks)) { + /* If prev style is OnTheSpot */ + if (IM_Context.im_style_orig & + (XIMPreeditCallbacks | XIMStatusCallbacks)) { + if (IM_Context.bEnable_OverTheSpot) { + IM_Context.im_style_now = + (XIMPreeditPosition | XIMStatusNothing); + } else if (IM_Context.bEnable_Root) { + IM_Context.im_style_now = + (XIMPreeditNothing | XIMStatusNothing); + } else { + SDL_SetError( + "Cannot support OverTheSpot or Root style mode XIC."); + return "SDL_IM_FLIP"; + } + IM_Context.preedit_attr_now = NULL; + IM_Context.status_attr_now = NULL; + } + else { + /* prev style is not OnTheSpot */ + IM_Context.im_style_now = IM_Context.im_style_orig; + IM_Context.preedit_attr_now = IM_Context.preedit_attr_orig; + IM_Context.status_attr_now = IM_Context.status_attr_orig; + } + + if (IM_Context.SDL_XIC) { + XDestroyIC(IM_Context.SDL_XIC); + IM_Context.SDL_XIC = XCreateIC(IM_Context.SDL_XIM, + XNInputStyle, IM_Context.im_style_now, + XNClientWindow, WMwindow, + XNFocusWindow, WMwindow, + IM_Context.preedit_attr_now ? XNPreeditAttributes : NULL, + IM_Context.preedit_attr_now, + IM_Context.status_attr_now ? XNStatusAttributes : NULL, + IM_Context.status_attr_now, + NULL); + + if (IM_Context.SDL_XIC == NULL) { + SDL_SetError( + "Cannot create original style mode XIC."); + return "SDL_IM_FLIP"; + } + } else { + SDL_SetError("Missing XIC"); + return "SDL_IM_FLIP"; + } + } + return NULL; + } + else { + /* Back to prev style mode, and just OnTheSpot mode. */ + /* if the prev style is not OnTheSpot */ + /* turn into OnTheSpot Mode */ + + /* If the now style is not OnTheSpot */ + XGetICValues(IM_Context.SDL_XIC, XNInputStyle, &IM_Context.im_style_now, NULL); + + if (IM_Context.im_style_now & (XIMPreeditCallbacks | XIMStatusCallbacks)) + ; + else { + /* if the orig style is OnTheSpot */ + if (IM_Context.im_style_orig & + (XIMPreeditCallbacks|XIMStatusCallbacks)) { + IM_Context.im_style_now = IM_Context.im_style_orig; + IM_Context.preedit_attr_now = IM_Context.preedit_attr_orig; + IM_Context.status_attr_now = IM_Context.status_attr_orig; + } + else { + IM_Context.im_style_now = + (XIMPreeditCallbacks | XIMStatusCallbacks); + IM_Context.preedit_attr_now = NULL; + IM_Context.status_attr_now = NULL; + } + + if (IM_Context.SDL_XIC && IM_Context.bEnable_OnTheSpot) { + XDestroyIC(IM_Context.SDL_XIC); + IM_Context.SDL_XIC = XCreateIC(IM_Context.SDL_XIM, + XNInputStyle, IM_Context.im_style_now, + XNClientWindow, WMwindow, + XNFocusWindow, WMwindow, + IM_Context.preedit_attr_now ? XNPreeditAttributes : NULL, + IM_Context.preedit_attr_now, + IM_Context.status_attr_now ? XNStatusAttributes : NULL, + IM_Context.status_attr_now, + NULL); + if (IM_Context.SDL_XIC == NULL) { + SDL_SetError("Cannot create OnTheSpot mode XIC."); + return "SDL_IM_FLIP"; + } + } else { + SDL_SetError("Missing XIC or Unsupport OnTheSopt mode."); + return "SDL_IM_FLIP"; + } + } + return NULL; + } + case SDL_IM_ONOFF: + if(alt) { + XVaNestedList list; + list = XVaCreateNestedList(0, XNPreeditState, XIMPreeditEnable, NULL); + if(XSetICValues(IM_Context.SDL_XIC, XNPreeditAttributes, list, NULL) != NULL) { + SDL_SetError("XSetICValues() failed. (XNPreeditState not supported.)"); + } + XFree(list); + } else { + XVaNestedList list; + list = XVaCreateNestedList(0, XNPreeditState, XIMPreeditDisable, NULL); + if(XSetICValues(IM_Context.SDL_XIC, XNPreeditAttributes, list, NULL) != NULL) { + SDL_SetError("XSetICValues() failed. (XNPreeditState not supported.)"); + } + XFree(list); + } + return NULL; + default: + SDL_SetError("X11_SetIMValues: unknown enum type: %d", value); + return "Unknown enum type"; + } + } + else { + SDL_SetError("SDL_XIC is NULL"); + return "SDL_XIC is NULL"; + } +} + +char *X11_GetIMValues(_THIS, SDL_imvalue value, int *alt) +{ + char *rec; + rec = 0; + + if (IM_Context.SDL_XIC) { + switch (value) { + case SDL_IM_ENABLE: + if (IM_Context.ic_focus) { + *alt = 1; + return NULL; + } else { + *alt = 0; + return NULL; + } + case SDL_IM_FLIP: + rec = XGetICValues(IM_Context.SDL_XIC, XNInputStyle, &IM_Context.im_style_now, NULL); + if (rec) { + SDL_SetError("Can not get IC values"); + return rec; + } + + if (IM_Context.im_style_now != (XIMPreeditCallbacks | XIMStatusCallbacks) && + IM_Context.im_style_now != (XIMPreeditArea | XIMStatusArea)) { + *alt = 1; + return NULL; + } + else { + *alt = 0; + return NULL; + } + case SDL_IM_ONOFF: + { + XIMPreeditState state; + if(XGetICValues(IM_Context.SDL_XIC, XNPreeditState, &state, NULL) != NULL) { + if(state == XIMPreeditEnable) { + *alt = 1; + } else { + *alt = 0; + } + return NULL; + } + } + default: + SDL_SetError("X11_GetIMValues: unknown enum type: %d", value); + return "Unknown enum type"; + } + } + else { + SDL_SetError("SDL_XIC is NULL."); + return "SDL_XIC is NULL"; + } +} + +int X11_FlushIMString(_THIS, void *buffer) +{ + int result; + if (buffer && IM_Context.im_compose_len) { + if (SDL_TranslateUNICODE) { + int i = 0; + Uint16* b = (Uint16*)buffer; + while (i < IM_Context.im_compose_len) { + b[i] = IM_Context.string.im_wide_char_buffer[i]; + ++i; + } + } + else + memcpy(buffer, IM_Context.string.im_multi_byte_buffer, IM_Context.im_compose_len); + + result = IM_Context.im_compose_len; + IM_Context.im_compose_len = 0; + } + else + result = IM_Context.im_compose_len; + + return result; +} + +#else /* ! ENABLE_IM_EVENT */ +/* Fill with null implementation */ + +int X11_SetIMPosition(_THIS, int x, int y) +{ + return 0; +} +char *X11_SetIMValues(_THIS, SDL_imvalue value, int alt) +{ + return NULL; +} +char *X11_GetIMValues(_THIS, SDL_imvalue value, int *alt) +{ + return NULL; +} +int X11_FlushIMString(_THIS, void *buffer) +{ + return 0; +} +#endif diff --git a/vs2015/sdl/src/video/x11/SDL_x11events_c.h b/vs2015/sdl/src/video/x11/SDL_x11events_c.h index fe26d9c26..3b1147a1c 100644 --- a/vs2015/sdl/src/video/x11/SDL_x11events_c.h +++ b/vs2015/sdl/src/video/x11/SDL_x11events_c.h @@ -32,3 +32,7 @@ extern void X11_SetKeyboardState(Display *display, const char *key_vec); extern int X11_PendingConfigureNotifyWidth; extern int X11_PendingConfigureNotifyHeight; +extern int X11_SetIMPosition(_THIS, int x, int y); +extern char *X11_SetIMValues(_THIS, SDL_imvalue value, int alt); +extern char *X11_GetIMValues(_THIS, SDL_imvalue value, int *alt); +extern int X11_FlushIMString(_THIS, void *buffer); diff --git a/vs2015/sdl/src/video/x11/SDL_x11video.c b/vs2015/sdl/src/video/x11/SDL_x11video.c index 482314ed1..b8a2e3c83 100644 --- a/vs2015/sdl/src/video/x11/SDL_x11video.c +++ b/vs2015/sdl/src/video/x11/SDL_x11video.c @@ -68,6 +68,41 @@ static int X11_SetColors(_THIS, int firstcolor, int ncolors, static int X11_SetGammaRamp(_THIS, Uint16 *ramp); static void X11_VideoQuit(_THIS); +#ifdef ENABLE_IM_EVENT +#include +/* When this flag is enabled, force to use the default visual. + Some Input Methods fails to create preedit window in other visuals. + kinput2 fails. skkinput is OK. */ +#define FORCE_USE_DEFAULT_VISUAL + +/* XIM initialization function */ +static int xim_init(_THIS); +static void xim_free(_THIS); + +/* IM CallBack Function */ +static int ef_height=0, ef_width=0, ef_ascent=0; + +typedef struct { + XIMStyle style; + char *description; +} im_style_t; + +/* Enumeration of XIM input style */ +static im_style_t im_styles[] = { + { XIMPreeditNothing | XIMStatusNothing, "Root" }, + { XIMPreeditPosition | XIMStatusNothing, "OverTheSpot" }, + { XIMPreeditArea | XIMStatusArea, "OffTheSpot" }, + { XIMPreeditCallbacks| XIMStatusCallbacks, "OnTheSpot" }, + { (XIMStyle)0, NULL }}; + +#include +#include + +/* Locale-specific data taken by locale_init and xim_init */ +static char *im_name; +static char *lc_ctype; + +#endif /* ENABLE_IM_EVENT */ /* X11 driver bootstrap functions */ @@ -173,6 +208,12 @@ static SDL_VideoDevice *X11_CreateDevice(int devindex) device->InitOSKeymap = X11_InitOSKeymap; device->PumpEvents = X11_PumpEvents; + device->SetIMPosition = X11_SetIMPosition; + device->SetIMValues = X11_SetIMValues; + device->GetIMValues = X11_GetIMValues; + device->FlushIMString = X11_FlushIMString; + device->GetIMInfo = X11_GetIMInfo; + device->free = X11_DeleteDevice; } @@ -412,6 +453,12 @@ static void create_aux_windows(_THIS, const unsigned int force) XFree(hints); X11_SetCaptionNoLock(this, this->wm_title, this->wm_icon); +#ifdef ENABLE_IM_EVENT + if(!xim_init(this)) { + SDL_SetError("Error: Can not initialize XIM."); + } +#endif + app_event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask | PropertyChangeMask | StructureNotifyMask | KeymapStateMask; XSelectInput(SDL_Display, WMwindow, app_event_mask); @@ -1632,3 +1679,418 @@ void X11_VideoQuit(_THIS) #endif } +#ifdef ENABLE_IM_EVENT +//#define DEBUG_XEVENTS + +void destroy_callback_func(XIM current_ic, XPointer client_data, XPointer call_data) +{ + SDL_VideoDevice *this = current_video; + xim_free(this); +} + +void im_callback(XIM xim, XPointer client_data, XPointer call_data) +{ + XIMStyle input_style; + XIMStyles *xim_styles = NULL; + XIMCallback destroy; + int j; + + XPoint spot; + char *env_sdlim_style; + + SDL_VideoDevice *this = current_video; + XVaNestedList preedit_attr = NULL; + + /* + * Open connection to IM server. + */ + { + char *env_xmodifiers = getenv("XMODIFIERS"); + if (env_xmodifiers != NULL) + im_name = XSetLocaleModifiers(env_xmodifiers); + else + fprintf(stderr, "Warning: XMODIFIERS is unspecified\n"); + } + if (! (IM_Context.SDL_XIM = XOpenIM(SDL_Display, NULL, NULL, NULL))) { + SDL_SetError("Cannot open the connection to XIM server."); +#ifdef DEBUG_XEVENTS + printf("Cannot open the connection to XIM server.\n"); +#endif + return; + } + + destroy.callback = (XIMProc)destroy_callback_func; + destroy.client_data = NULL; + XSetIMValues(IM_Context.SDL_XIM, XNDestroyCallback, &destroy, NULL); + + /* + * Detect the input style supported by XIM server. + */ + if (XGetIMValues(IM_Context.SDL_XIM, XNQueryInputStyle, &xim_styles, NULL) || !xim_styles) { +#ifdef DEBUG_XEVENTS + printf("input method doesn't support any style."); +#endif + SDL_SetError("input method doesn't support any style."); + XCloseIM(IM_Context.SDL_XIM); + return; + } +#ifdef DEBUG_XEVENTS + else { + int i; + for (i=0; icount_styles; i++) { + for (j=0; im_styles[j].description!=NULL; j++) { + if (im_styles[j].style == xim_styles->supported_styles[i]) { + printf("XIM server support input_style = %s\n", im_styles[j].description); + break; + } + } + if (im_styles[j].description==NULL) + printf("XIM server support unknown input_style = %x\n", (unsigned)(xim_styles->supported_styles[i])); + } + } +#endif + + /* + * Setting the XIM style. + */ + /* OverTheSpot input_style as the default */ + input_style = 0; + for (j = 0; im_styles[j].description != NULL; j++) { + if (! strcmp(im_styles[j].description, "OverTheSpot")) { + input_style = im_styles[j].style; + IM_Context.bEnable_OverTheSpot = 1; +#ifdef DEBUG_XEVENTS + printf("OverTheSpot mode supported.\n"); +#endif + } + if (! strcmp(im_styles[j].description, "OnTheSpot")) { + IM_Context.bEnable_OnTheSpot = 1; +#ifdef DEBUG_XEVENTS + printf("OnTheSpot mode supported.\n"); +#endif + } + if (! strcmp(im_styles[j].description, "Root")) { + IM_Context.bEnable_Root = 1; +#ifdef DEBUG_XEVENTS + printf("Root mode supported.\n"); +#endif + } + } + + /* If not support OverTheSpot mode, use Root mode. */ + if (input_style != (XIMPreeditPosition | XIMStatusNothing)) { + SDL_SetError("The XIM doesn't support OverTheSpot mode."); + for (j=0; im_styles[j].description!=NULL; j++) { + if (! strcmp(im_styles[j].description, "Root")) { +#ifdef DEBUG_XEVENTS + printf("Root\n"); +#endif + input_style = im_styles[j].style; + } + } + + /* If not support Root mode, use OnTheSpot mode.*/ + if (input_style != (XIMPreeditNothing | XIMStatusNothing)) { + SDL_SetError("The XIM doesn't support OverTheSpot and Root mode."); + for (j = 0; im_styles[j].description != NULL; j++) { + if (! strcmp(im_styles[j].description, "OnTheSpot")) { +#ifdef DEBUG_XEVENTS + printf("OnTheSpot\n"); +#endif + input_style = im_styles[j].style; + } + } + if (input_style != (XIMPreeditCallbacks | XIMStatusCallbacks)) { + SDL_SetError("The XIM doesn't support OverTheSpot, Root, and OnTheSpot mode."); + } + } + } + + XFree(xim_styles); + + /* If the environmet variable SDLIM_STYLE is set, + override the style setting. + In current SDL-IM implementation, this is required + since some Input Methods don't work in OverTheSpot style + despite they tell they support OverTheSpot style. */ + env_sdlim_style = getenv("SDLIM_STYLE"); + if (env_sdlim_style != NULL) { +#ifdef DEBUG_XEVENTS + printf("SDLIM_STYLE=%s\n", env_sdlim_style); +#endif + if (strcmp(env_sdlim_style, "Root") == 0) { + input_style = (XIMPreeditNothing | XIMStatusNothing); + } else if (strcmp(env_sdlim_style, "OverTheSpot") == 0) { + input_style = (XIMPreeditPosition | XIMStatusNothing); + } else if (strcmp(env_sdlim_style, "OnTheSpot") == 0) { + input_style = (XIMPreeditCallbacks | XIMStatusCallbacks); + } + } +#ifdef DEBUG_XEVENTS + else + printf("SDLIM_STYLE= \n"); +#endif + +#ifdef DEBUG_XEVENTS + /* print which mode used. */ + switch(input_style) + { + case (XIMPreeditNothing | XIMStatusNothing): + printf("use Root mode.\n"); + break; + case (XIMPreeditPosition | XIMStatusNothing): + printf("use OverTheSpot mode.\n"); + break; + case (XIMPreeditCallbacks | XIMStatusCallbacks): + printf("use OnTheSpot mode.\n"); + break; + case (XIMPreeditArea|XIMStatusArea): + printf("use OffTheSpot mode.\n"); + break; + default: + printf("use Unknown mode.\n"); + break; + } +#endif + + /* for XIMPreeditPosition(OverTheSpot) */ + preedit_attr = 0; + spot.x = 0; + spot.y = 2*ef_height + 3*(ef_ascent+5); + preedit_attr = XVaCreateNestedList(0, + XNSpotLocation, &spot, + (IM_Context.fontset) ? XNFontSet : NULL, + IM_Context.fontset, + NULL); + + /* + * Create IC. + */ + IM_Context.SDL_XIC = XCreateIC(IM_Context.SDL_XIM, + XNInputStyle, input_style, + XNClientWindow, WMwindow, + XNFocusWindow, WMwindow, + (input_style & (XIMPreeditPosition | XIMStatusNothing) && + preedit_attr) ? + XNPreeditAttributes : NULL, preedit_attr, + NULL); + + if(IM_Context.SDL_XIC == NULL) { + // try Root mode +#ifdef DEBUG_XEVENTS + printf("Cannot create XIC. Try Root mode.\n"); +#endif + input_style = (XIMPreeditNothing | XIMStatusNothing); + IM_Context.SDL_XIC = XCreateIC(IM_Context.SDL_XIM, + XNInputStyle, input_style, + XNClientWindow, WMwindow, + XNFocusWindow, WMwindow, + NULL); + + if (IM_Context.SDL_XIC == NULL) { +#ifdef DEBUG_XEVENTS + printf("Cannot create XIC. "); +#endif + SDL_SetError("Cannot create XIC."); + return; + } + } + IM_Context.preedit_attr_now = preedit_attr; + IM_Context.im_style_now = input_style; + + XSetICFocus(IM_Context.SDL_XIC); + XUnsetICFocus(IM_Context.SDL_XIC); + + return; +} + +int create_fontset(void) +{ + int i, fsize, charset_count, fontset_count = 1; + char *s1, *s2; + char **charset_list, *def_string; + XFontStruct **font_structs; + char *fontset_name = NULL; + SDL_VideoDevice *this = current_video; + + fontset_name = getenv("SDLIM_FONTSET"); + if (fontset_name == NULL || !isprint(*fontset_name)) { +#ifdef DEBUG_XEVENTS + printf("Please set environment variable: SDLIM_FONTSET\nbash ex.\n\texport SDLIM_FONTSET=*-ISO8859-1,*-BIG5-0\n"); +#endif + //SDL_SetError("Please set environment variable: SDLIM_FONTSET\nbash ex.\n\texport SDLIM_FONTSET=*-ISO8859-1,*-BIG5-0"); + //fontset_name = "*-ISO8859-1"; + fontset_name = "-*-fixed-medium-r-normal--16-*-*-*"; + } +#ifdef DEBUG_XEVENTS + printf("IM fontset: %s\n", fontset_name); +#endif + + /* + * Calculate the number of fonts. + */ + s1 = fontset_name; + while ((s2=strchr(s1, ',')) != NULL) { + s2 ++; + while (isspace((int)(*s2))) + s2++; + if (*s2 && *s2 != ',') { + fontset_count++; + s1 = s2; + } + else { + break; + *s1 = '\0'; + } + } + /* + * Create fontset and extract font information. + */ + IM_Context.fontset = XCreateFontSet(SDL_Display, fontset_name, &charset_list, &charset_count, &def_string); + if (charset_count || !IM_Context.fontset) { + SDL_SetError("Error: cannot create fontset."); +#ifdef DEBUG_XEVENTS + printf("Error: cannot create fontset. %d %d %s\n", charset_count, IM_Context.fontset, def_string); +#endif + return 0; + } + if (fontset_count != XFontsOfFontSet(IM_Context.fontset, &font_structs, &charset_list)) { + SDL_SetError("Warning: fonts not consistant to fontset."); +#ifdef DEBUG_XEVENTS + printf("Warning: fonts not consistant to fontset.\n"); +#endif + fontset_count = XFontsOfFontSet(IM_Context.fontset, &font_structs, &charset_list); + } + + + for (i = 0; i < fontset_count; i++) { + fsize = font_structs[i]->max_bounds.width / 2; + if (fsize > ef_width) + ef_width = fsize; + fsize = font_structs[i]->ascent + font_structs[i]->descent; + if (fsize > ef_height) { + ef_height = fsize; + ef_ascent = font_structs[i]->ascent; + } + } + + if (charset_list) + XFreeStringList(charset_list); + + return 0; +} + +int locale_init(void) +{ + char buf[1024]; + + if ((lc_ctype = setlocale(LC_CTYPE, "")) == NULL) { + SDL_SetError("setlocale LC_CTYPE false."); +#ifdef DEBUG_XEVENTS + printf("setlocale LC_CTYPE false.\n"); +#endif + return 0; + } + + if (XSupportsLocale() != True) { + SDL_SetError("XSupportsLocale false."); +#ifdef DEBUG_XEVENTS + printf("XSupportsLocale false.\n"); +#endif + return 0; + } + + if (im_name){ + sprintf(buf, "@im=%s", im_name); + } + else { + /* clean up buf if environment variable wasn't specified. */ + memset(buf, 0, 1024); + } + + if (XSetLocaleModifiers(buf) == NULL) { + SDL_SetError("XSetLocaleModifiers false."); +#ifdef DEBUG_XEVENTS + SDL_SetError("XSetLocaleModifiers false.\n"); +#endif + return 0; + } + + create_fontset(); + + return 1; +} + +int xim_init(_THIS) +{ + IM_Context.SDL_XIM = NULL; + IM_Context.SDL_XIC = NULL; + IM_Context.string.im_wide_char_buffer = '\0'; + IM_Context.im_buffer_len = 0; + IM_Context.im_compose_len = 0; + IM_Context.ic_focus = 0; + IM_Context.im_enable = 0; + IM_Context.bEnable_OverTheSpot = 0; + IM_Context.bEnable_OnTheSpot = 0; + IM_Context.bEnable_Root = 0; + IM_Context.preedit_attr_orig = NULL; + IM_Context.status_attr_orig = NULL; + IM_Context.im_style_orig = 0; + IM_Context.preedit_attr_now = NULL; + IM_Context.status_attr_now = NULL; + IM_Context.im_style_now = 0; + IM_Context.fontset = NULL; + + if (!locale_init()) { + return 0; + } + + if (XRegisterIMInstantiateCallback(SDL_Display, NULL, NULL, NULL, (XIMProc)im_callback, NULL) != True) { + SDL_SetError("XRegisterIMInstantiateCallback false."); +#ifdef DEBUG_XEVENTS + printf("XRegisterIMInstantiateCallback false.\n"); +#endif + return 0; + } + return 1; +} + +static void xim_free(_THIS) +{ + if (IM_Context.SDL_XIM) { + XCloseIM(IM_Context.SDL_XIM); + } + if (IM_Context.SDL_XIC) { + XDestroyIC(IM_Context.SDL_XIC); + } + IM_Context.SDL_XIC = NULL; + IM_Context.SDL_XIM = NULL; + IM_Context.ic_focus = 0; + + IM_Context.im_compose_len = 0; + IM_Context.im_buffer_len = 0; + if (IM_Context.string.im_wide_char_buffer) { + free(IM_Context.string.im_wide_char_buffer); + } + /*IM_Context.string.im_wide_char_buffer = '\0';*/ + + IM_Context.bEnable_OverTheSpot = 0; + IM_Context.bEnable_OnTheSpot = 0; + IM_Context.bEnable_Root = 0; + + if (IM_Context.preedit_attr_orig) { + XFree(IM_Context.preedit_attr_orig); + } + if ((IM_Context.preedit_attr_now != IM_Context.preedit_attr_orig) && IM_Context.preedit_attr_now) { + XFree(IM_Context.preedit_attr_now); + } + IM_Context.preedit_attr_now = NULL; + IM_Context.preedit_attr_orig = NULL; + + if (IM_Context.fontset) { + XFreeFontSet(SDL_Display, IM_Context.fontset); + } + IM_Context.fontset = NULL; +} + +#endif /* ENABLE_IM_EVENT */ diff --git a/vs2015/sdl/src/video/x11/SDL_x11video.h b/vs2015/sdl/src/video/x11/SDL_x11video.h index f347560d6..e99956b27 100644 --- a/vs2015/sdl/src/video/x11/SDL_x11video.h +++ b/vs2015/sdl/src/video/x11/SDL_x11video.h @@ -154,6 +154,37 @@ struct SDL_PrivateVideoData { /* Screensaver settings */ int allow_screensaver; + + /* IM context */ + struct { + XIM SDL_XIM; + XIC SDL_XIC; + union { + char *im_multi_byte_buffer; + wchar_t *im_wide_char_buffer; + } string; + int im_buffer_len; + int im_compose_len; + + /* Switch of XIM InputContext */ + char ic_focus; + char im_enable; + + /* Decide if OverTheSpot, OnTheSpot, and Root input style is enabled. */ + char bEnable_OverTheSpot; + char bEnable_OnTheSpot; + char bEnable_Root; + + XVaNestedList preedit_attr_orig; + XVaNestedList status_attr_orig; + XIMStyle im_style_orig; + + XVaNestedList preedit_attr_now; + XVaNestedList status_attr_now; + XIMStyle im_style_now; + + XFontSet fontset; + } IM_Context; }; /* Old variable names */ @@ -206,6 +237,8 @@ struct SDL_PrivateVideoData { #define SDL_iconcolors (this->hidden->iconcolors) #define allow_screensaver (this->hidden->allow_screensaver) +#define IM_Context (this->hidden->IM_Context) + /* Some versions of XFree86 have bugs - detect if this is one of them */ #define BUGGY_XFREE86(condition, buggy_version) \ ((SDL_strcmp(ServerVendor(SDL_Display), "The XFree86 Project, Inc") == 0) && \ diff --git a/vs2015/sdl/src/video/x11/SDL_x11wm.c b/vs2015/sdl/src/video/x11/SDL_x11wm.c index 972504728..efeb337ba 100644 --- a/vs2015/sdl/src/video/x11/SDL_x11wm.c +++ b/vs2015/sdl/src/video/x11/SDL_x11wm.c @@ -435,3 +435,27 @@ int X11_GetWMInfo(_THIS, SDL_SysWMinfo *info) return(-1); } } + +int X11_GetIMInfo(_THIS, SDL_SysIMinfo *info) +{ + + if ( info->version.major <= SDL_MAJOR_VERSION ) { + if ( SDL_VERSIONNUM(info->version.major, + info->version.minor, + info->version.patch) >= + SDL_VERSIONNUM(1, 2, 8) ) { +#ifdef ENABLE_IM_EVENT + info->xim = IM_Context.SDL_XIM; + info->xic = &IM_Context.SDL_XIC; +#else + info->xim = NULL; + info->xic = NULL; +#endif + } + return(1); + } else { + SDL_SetError("Application not compiled with SDL %d.%d\n", + SDL_MAJOR_VERSION, SDL_MINOR_VERSION); + return(-1); + } +} diff --git a/vs2015/sdl/src/video/x11/SDL_x11wm_c.h b/vs2015/sdl/src/video/x11/SDL_x11wm_c.h index f85477bba..bdbfd0d8c 100644 --- a/vs2015/sdl/src/video/x11/SDL_x11wm_c.h +++ b/vs2015/sdl/src/video/x11/SDL_x11wm_c.h @@ -32,3 +32,6 @@ extern SDL_GrabMode X11_GrabInputNoLock(_THIS, SDL_GrabMode mode); extern SDL_GrabMode X11_GrabInput(_THIS, SDL_GrabMode mode); extern int X11_GetWMInfo(_THIS, SDL_SysWMinfo *info); +/* Functions to IM */ +extern int X11_GetIMInfo(_THIS, SDL_SysIMinfo *info); +