diff --git a/CMakeLists.txt b/CMakeLists.txt index f2ffffeec31..4ee277b9248 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -318,6 +318,9 @@ mark_as_advanced(WITH_LIBMV_SCHUR_SPECIALIZATIONS) option(WITH_FREESTYLE "Enable Freestyle (advanced edges rendering)" ON) # Misc +if(WIN32) + option(WITH_INPUT_IME "Enable Input Method Editor (IME) for complex Asian character input" ON) +endif() option(WITH_INPUT_NDOF "Enable NDOF input devices (SpaceNavigator and friends)" ${_init_INPUT_NDOF}) option(WITH_RAYOPTIMIZATION "Enable use of SIMD (SSE) optimizations for the raytracer" ON) option(WITH_OPENNL "Enable use of Open Numerical Library" ON) @@ -1151,6 +1154,10 @@ elseif(WIN32) list(APPEND PLATFORM_LINKLIBS ws2_32 vfw32 winmm kernel32 user32 gdi32 comdlg32 advapi32 shfolder shell32 ole32 oleaut32 uuid psapi) + if(WITH_INPUT_IME) + list(APPEND PLATFORM_LINKLIBS imm32) + endif() + add_definitions( -D_CRT_NONSTDC_NO_DEPRECATE -D_CRT_SECURE_NO_DEPRECATE @@ -1506,6 +1513,11 @@ elseif(WIN32) endif() list(APPEND PLATFORM_LINKLIBS -lshell32 -lshfolder -lgdi32 -lmsvcrt -lwinmm -lmingw32 -lm -lws2_32 -lz -lstdc++ -lole32 -luuid -lwsock32 -lpsapi) + + if(WITH_INPUT_IME) + list(APPEND PLATFORM_LINKLIBS -limm32) + endif() + set(PLATFORM_CFLAGS "-pipe -funsigned-char -fno-strict-aliasing") if(WITH_MINGW64) diff --git a/SConstruct b/SConstruct index 4c2097f8879..4dacf89fcb0 100644 --- a/SConstruct +++ b/SConstruct @@ -266,6 +266,7 @@ if 'blenderlite' in B.targets: target_env_defs['WITH_BF_BOOLEAN'] = False target_env_defs['WITH_BF_REMESH'] = False target_env_defs['WITH_BF_PYTHON'] = False + target_env_defs['WITH_BF_IME'] = False target_env_defs['WITH_BF_3DMOUSE'] = False target_env_defs['WITH_BF_LIBMV'] = False target_env_defs['WITH_BF_FREESTYLE'] = False diff --git a/build_files/scons/config/win32-mingw-config.py b/build_files/scons/config/win32-mingw-config.py index 85aa72af19c..0fb84bfa4a5 100644 --- a/build_files/scons/config/win32-mingw-config.py +++ b/build_files/scons/config/win32-mingw-config.py @@ -170,6 +170,8 @@ BF_BOOST_LIBPATH = '${BF_BOOST}/lib' WITH_BF_RAYOPTIMIZATION = True BF_RAYOPTIMIZATION_SSE_FLAGS = ['-msse'] +WITH_BF_IME = True + WITH_BF_OPENMP = True #CUDA @@ -198,6 +200,9 @@ CC_WARN = [ '-Wall' ] LLIBS = ['-lshell32', '-lshfolder', '-lgdi32', '-lmsvcrt', '-lwinmm', '-lmingw32', '-lm', '-lws2_32', '-lz', '-lstdc++','-lole32','-luuid', '-lwsock32', '-lpsapi'] +if WITH_BF_IME: + LLIBS.append('-limm32') + PLATFORM_LINKFLAGS = ['-Xlinker', '--stack=2097152'] ## DISABLED, causes linking errors! diff --git a/build_files/scons/config/win32-vc-config.py b/build_files/scons/config/win32-vc-config.py index 967e9636fc2..d447a1ec08e 100644 --- a/build_files/scons/config/win32-vc-config.py +++ b/build_files/scons/config/win32-vc-config.py @@ -151,6 +151,8 @@ BF_OPENCOLLADA_INC = '${BF_OPENCOLLADA}/include/opencollada' BF_OPENCOLLADA_LIB = 'OpenCOLLADAStreamWriter OpenCOLLADASaxFrameworkLoader OpenCOLLADAFramework OpenCOLLADABaseUtils GeneratedSaxParser MathMLSolver xml pcre buffer ftoa' BF_OPENCOLLADA_LIBPATH = '${BF_OPENCOLLADA}/lib/opencollada' +WITH_BF_IME = True + WITH_BF_3DMOUSE = True WITH_BF_OPENMP = True @@ -237,6 +239,9 @@ CXX_WARN = [] LLIBS = ['ws2_32', 'vfw32', 'winmm', 'kernel32', 'user32', 'gdi32', 'comdlg32', 'advapi32', 'shfolder', 'shell32', 'ole32', 'oleaut32', 'uuid', 'psapi'] +if WITH_BF_IME: + LLIBS.append('imm32') + PLATFORM_LINKFLAGS = ['/SUBSYSTEM:CONSOLE','/MACHINE:IX86','/STACK:2097152','/INCREMENTAL:NO', '/LARGEADDRESSAWARE', '/NODEFAULTLIB:msvcrt.lib', '/NODEFAULTLIB:msvcmrt.lib', '/NODEFAULTLIB:msvcurt.lib', '/NODEFAULTLIB:msvcrtd.lib'] # # Todo diff --git a/build_files/scons/config/win64-mingw-config.py b/build_files/scons/config/win64-mingw-config.py index 0314ab13d07..f93be284813 100644 --- a/build_files/scons/config/win64-mingw-config.py +++ b/build_files/scons/config/win64-mingw-config.py @@ -170,6 +170,8 @@ BF_BOOST_LIBPATH = '${BF_BOOST}/lib' WITH_BF_RAYOPTIMIZATION = True BF_RAYOPTIMIZATION_SSE_FLAGS = ['-mmmx', '-msse', '-msse2'] +WITH_BF_IME = True + WITH_BF_OPENMP = True #Freestyle @@ -193,6 +195,9 @@ CC_WARN = [ '-Wall' ] LLIBS = ['-lshell32', '-lshfolder', '-lgdi32', '-lmsvcrt', '-lwinmm', '-lmingw32', '-lm', '-lws2_32', '-lz', '-lstdc++','-lole32','-luuid', '-lwsock32', '-lpsapi', '-lpthread'] +if WITH_BF_IME: + LLIBS.append('-limm32') + PLATFORM_LINKFLAGS = ['-Xlinker', '--stack=2097152'] ## DISABLED, causes linking errors! diff --git a/build_files/scons/config/win64-vc-config.py b/build_files/scons/config/win64-vc-config.py index 74d8c204963..b8e4f3d70fe 100644 --- a/build_files/scons/config/win64-vc-config.py +++ b/build_files/scons/config/win64-vc-config.py @@ -154,6 +154,8 @@ BF_OPENCOLLADA_INC = '${BF_OPENCOLLADA}/include/opencollada' BF_OPENCOLLADA_LIB = 'OpenCOLLADAStreamWriter OpenCOLLADASaxFrameworkLoader OpenCOLLADAFramework OpenCOLLADABaseUtils GeneratedSaxParser MathMLSolver xml pcre buffer ftoa' BF_OPENCOLLADA_LIBPATH = '${BF_OPENCOLLADA}/lib/opencollada' +WITH_BF_IME = True + WITH_BF_3DMOUSE = True WITH_BF_OPENMP = True @@ -244,6 +246,9 @@ CXX_WARN = [] LLIBS = ['ws2_32', 'vfw32', 'winmm', 'kernel32', 'user32', 'gdi32', 'comdlg32', 'advapi32', 'shfolder', 'shell32', 'ole32', 'oleaut32', 'uuid', 'psapi'] +if WITH_BF_IME: + LLIBS.append('imm32') + PLATFORM_LINKFLAGS = ['/SUBSYSTEM:CONSOLE','/MACHINE:X64','/STACK:2097152','/OPT:NOREF','/INCREMENTAL:NO', '/NODEFAULTLIB:msvcrt.lib', '/NODEFAULTLIB:msvcmrt.lib', '/NODEFAULTLIB:msvcurt.lib', '/NODEFAULTLIB:msvcrtd.lib'] BF_CYCLES_CUDA_ENV="C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" diff --git a/build_files/scons/tools/btools.py b/build_files/scons/tools/btools.py index cf9962d1bff..eb5036f9996 100644 --- a/build_files/scons/tools/btools.py +++ b/build_files/scons/tools/btools.py @@ -174,6 +174,7 @@ def validate_arguments(args, bc): 'WITH_BF_CXX_GUARDEDALLOC', 'WITH_BF_JEMALLOC', 'WITH_BF_STATICJEMALLOC', 'BF_JEMALLOC', 'BF_JEMALLOC_INC', 'BF_JEMALLOC_LIBPATH', 'BF_JEMALLOC_LIB', 'BF_JEMALLOC_LIB_STATIC', 'BUILDBOT_BRANCH', + 'WITH_BF_IME', 'WITH_BF_3DMOUSE', 'WITH_BF_STATIC3DMOUSE', 'BF_3DMOUSE', 'BF_3DMOUSE_INC', 'BF_3DMOUSE_LIB', 'BF_3DMOUSE_LIBPATH', 'BF_3DMOUSE_LIB_STATIC', 'WITH_BF_CYCLES', 'WITH_BF_CYCLES_CUDA_BINARIES', 'BF_CYCLES_CUDA_NVCC', 'BF_CYCLES_CUDA_NVCC', 'WITH_BF_CYCLES_CUDA_THREADED_COMPILE', 'BF_CYCLES_CUDA_ENV', 'WITH_BF_OIIO', 'WITH_BF_STATICOIIO', 'BF_OIIO', 'BF_OIIO_INC', 'BF_OIIO_LIB', 'BF_OIIO_LIB_STATIC', 'BF_OIIO_LIBPATH', @@ -507,6 +508,8 @@ def read_opts(env, cfg, args): (BoolVariable('WITH_BF_PLAYER', 'Build blenderplayer if true', False)), (BoolVariable('WITH_BF_NOBLENDER', 'Do not build blender if true', False)), + (BoolVariable('WITH_BF_IME', 'Enable Input Method Editor (IME) for complex Asian character input', False)), + (BoolVariable('WITH_BF_3DMOUSE', 'Build blender with support of 3D mouses', False)), (BoolVariable('WITH_BF_STATIC3DMOUSE', 'Staticly link to 3d mouse library', False)), ('BF_3DMOUSE', '3d mouse library base path', ''), diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt index 3ce269c2b4f..dc55a81b0d8 100644 --- a/intern/ghost/CMakeLists.txt +++ b/intern/ghost/CMakeLists.txt @@ -280,6 +280,16 @@ elseif(WIN32) ) endif() + if(WITH_INPUT_IME) + add_definitions(-DWITH_INPUT_IME) + + list(APPEND SRC + intern/GHOST_ImeWin32.cpp + + intern/GHOST_ImeWin32.h + ) + endif() + if(WITH_INPUT_NDOF) list(APPEND SRC intern/GHOST_NDOFManagerWin32.cpp diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h index 7b47f0526a2..c0f2651dd8b 100644 --- a/intern/ghost/GHOST_C-api.h +++ b/intern/ghost/GHOST_C-api.h @@ -897,6 +897,30 @@ extern int GHOST_UseNativePixels(void); */ extern float GHOST_GetNativePixelSize(GHOST_WindowHandle windowhandle); +/** + * Enable IME attached to the given window, i.e. allows user-input + * events to be dispatched to the IME. + * \param windowhandle Window handle of the caller + * \param x Requested x-coordinate of the rectangle + * \param y Requested y-coordinate of the rectangle + * \param w Requested width of the rectangle + * \param h Requested height of the rectangle + * \param complete Whether or not to complete the ongoing composition + * true: Start a new composition + * false: Move the IME windows to the given position without finishing it. + */ +extern void GHOST_BeginIME(GHOST_WindowHandle windowhandle, + GHOST_TInt32 x, + GHOST_TInt32 y, + GHOST_TInt32 w, + GHOST_TInt32 h, + int complete); +/** + * Disable the IME attached to the given window, i.e. prohibits any user-input + * events from being dispatched to the IME. + * \param windowhandle The window handle of the caller + */ +extern void GHOST_EndIME(GHOST_WindowHandle windowhandle); #ifdef __cplusplus } diff --git a/intern/ghost/GHOST_IWindow.h b/intern/ghost/GHOST_IWindow.h index 71dc193a81b..3f8215dc7c2 100644 --- a/intern/ghost/GHOST_IWindow.h +++ b/intern/ghost/GHOST_IWindow.h @@ -331,6 +331,29 @@ public: virtual float getNativePixelSize(void) = 0; +#ifdef WITH_INPUT_IME + /** + * Enable IME attached to the given window, i.e. allows user-input + * events to be dispatched to the IME. + * \param x Requested x-coordinate of the rectangle + * \param y Requested y-coordinate of the rectangle + * \param w Requested width of the rectangle + * \param h Requested height of the rectangle + * \param complete Whether or not to complete the ongoing composition + * true: Start a new composition + * false: Move the IME windows to the given position without finishing it. + */ + virtual void beginIME( + GHOST_TInt32 x, GHOST_TInt32 y, + GHOST_TInt32 w, GHOST_TInt32 h, + int completed) = 0; + + /** + * Disable the IME attached to the given window, i.e. prohibits any user-input + * events from being dispatched to the IME. + */ + virtual void endIME() = 0; +#endif /* WITH_INPUT_IME */ #ifdef WITH_CXX_GUARDEDALLOC MEM_CXX_CLASS_ALLOC_FUNCS("GHOST:GHOST_IWindow") diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h index 7333ba025a5..c4a7490c71c 100644 --- a/intern/ghost/GHOST_Types.h +++ b/intern/ghost/GHOST_Types.h @@ -190,6 +190,10 @@ typedef enum { GHOST_kEventTimer, + GHOST_kEventImeCompositionStart, + GHOST_kEventImeComposition, + GHOST_kEventImeCompositionEnd, + GHOST_kNumEventTypes } GHOST_TEventType; @@ -436,6 +440,22 @@ typedef struct { GHOST_TEventDataPtr data; } GHOST_TEventDragnDropData; +/** similar to wmImeData */ +typedef struct { + /** size_t */ + GHOST_TUserDataPtr result_len, composite_len; + /** char * utf8 encoding */ + GHOST_TUserDataPtr result, composite; + /** Cursor position in the IME composition. */ + int cursor_position; + /** Represents the position of the beginning of the selection */ + int target_start; + /** Represents the position of the end of the selection */ + int target_end; + /** custom temporal data */ + GHOST_TUserDataPtr tmp; +} GHOST_TEventImeData; + typedef struct { int count; GHOST_TUns8 **strings; diff --git a/intern/ghost/SConscript b/intern/ghost/SConscript index 5a4572c164d..a41fadf9ac7 100644 --- a/intern/ghost/SConscript +++ b/intern/ghost/SConscript @@ -154,6 +154,13 @@ if env['BF_GHOST_DEBUG']: else: sources.remove('intern' + os.sep + 'GHOST_EventPrinter.cpp') +if env['WITH_BF_IME']: + if window_system in ('win32-vc', 'win32-mingw', 'win64-vc', 'win64-mingw'): + defs.append('WITH_INPUT_IME') + else: + sources.remove('intern' + os.sep + 'GHOST_ImeWin32.h') + sources.remove('intern' + os.sep + 'GHOST_ImeWin32.cpp') + if env['WITH_BF_3DMOUSE']: defs.append('WITH_INPUT_NDOF') diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp index 5a2e638f01a..0da77ac5e20 100644 --- a/intern/ghost/intern/GHOST_C-api.cpp +++ b/intern/ghost/intern/GHOST_C-api.cpp @@ -915,3 +915,21 @@ float GHOST_GetNativePixelSize(GHOST_WindowHandle windowhandle) return 1.0f; } +#ifdef WITH_INPUT_IME + +void GHOST_BeginIME(GHOST_WindowHandle windowhandle, + GHOST_TInt32 x, GHOST_TInt32 y, + GHOST_TInt32 w, GHOST_TInt32 h, + int complete) +{ + GHOST_IWindow *window = (GHOST_IWindow *) windowhandle; + window->beginIME(x, y, w, h, complete); +} + +void GHOST_EndIME(GHOST_WindowHandle windowhandle) +{ + GHOST_IWindow *window = (GHOST_IWindow *) windowhandle; + window->endIME(); +} + +#endif /* WITH_INPUT_IME */ diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index 3b793fda968..4247c4f31ce 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -792,6 +792,15 @@ GHOST_Event *GHOST_SystemWin32::processWindowEvent(GHOST_TEventType type, GHOST_ return new GHOST_Event(system->getMilliSeconds(), type, window); } +#ifdef WITH_INPUT_IME +GHOST_Event *GHOST_SystemWin32::processImeEvent(GHOST_TEventType type, GHOST_IWindow *window, GHOST_TEventImeData *data) +{ + GHOST_System *system = (GHOST_System *)getSystem(); + return new GHOST_EventIME(system->getMilliSeconds(), type, window, data); +} +#endif + + GHOST_TSuccess GHOST_SystemWin32::pushDragDropEvent( GHOST_TEventType eventType, GHOST_TDragnDropTypes draggedObjectType, @@ -904,6 +913,7 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LRESULT lResult = 0; GHOST_SystemWin32 *system = ((GHOST_SystemWin32 *)getSystem()); + GHOST_EventManager *eventManager = system->getEventManager(); GHOST_ASSERT(system, "GHOST_SystemWin32::s_wndProc(): system not initialized"); if (hwnd) { @@ -912,8 +922,13 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, switch (msg) { // we need to check if new key layout has AltGr case WM_INPUTLANGCHANGE: + { system->handleKeyboardChange(); +#ifdef WITH_INPUT_IME + window->getImeInput()->SetInputLanguage(); +#endif break; + } //////////////////////////////////////////////////////////////////////// // Keyboard events, processed //////////////////////////////////////////////////////////////////////// @@ -949,6 +964,56 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, } break; } +#ifdef WITH_INPUT_IME + //////////////////////////////////////////////////////////////////////// + // IME events, processed, read more in GHOST_IME.h + //////////////////////////////////////////////////////////////////////// + case WM_IME_SETCONTEXT: + { + window->getImeInput()->SetInputLanguage(); + window->getImeInput()->CreateImeWindow(window->getHWND()); + window->getImeInput()->CleanupComposition(window->getHWND()); + window->getImeInput()->CheckFirst(window->getHWND()); + break; + } + case WM_IME_STARTCOMPOSITION: + { + eventHandled = true; + /* remove input event before start comp event, avoid redundant input */ + eventManager->removeTypeEvents(GHOST_kEventKeyDown, window); + window->getImeInput()->CreateImeWindow(window->getHWND()); + window->getImeInput()->ResetComposition(window->getHWND()); + event = processImeEvent( + GHOST_kEventImeCompositionStart, + window, + &window->getImeInput()->eventImeData); + break; + } + case WM_IME_COMPOSITION: + { + eventHandled = true; + window->getImeInput()->UpdateImeWindow(window->getHWND()); + window->getImeInput()->UpdateInfo(window->getHWND()); + event = processImeEvent( + GHOST_kEventImeComposition, + window, + &window->getImeInput()->eventImeData); + break; + } + case WM_IME_ENDCOMPOSITION: + { + eventHandled = true; + /* remove input event after end comp event, avoid redundant input */ + eventManager->removeTypeEvents(GHOST_kEventKeyDown, window); + window->getImeInput()->ResetComposition(window->getHWND()); + window->getImeInput()->DestroyImeWindow(window->getHWND()); + event = processImeEvent( + GHOST_kEventImeCompositionEnd, + window, + &window->getImeInput()->eventImeData); + break; + } +#endif /* WITH_INPUT_IME */ //////////////////////////////////////////////////////////////////////// // Keyboard events, ignored //////////////////////////////////////////////////////////////////////// diff --git a/intern/ghost/intern/GHOST_SystemWin32.h b/intern/ghost/intern/GHOST_SystemWin32.h index 79fed06c6a5..cb3b8ee3cfc 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.h +++ b/intern/ghost/intern/GHOST_SystemWin32.h @@ -306,6 +306,15 @@ protected: */ static GHOST_Event *processWindowEvent(GHOST_TEventType type, GHOST_IWindow *window); + /** + * Creates a IME event. + * \param type The type of event to create. + * \param window The window receiving the event (the active window). + * \param data IME data. + * \return The event created. + */ + static GHOST_Event *processImeEvent(GHOST_TEventType type, GHOST_IWindow *window, GHOST_TEventImeData *data); + /** * Handles minimum window size. * \param minmax The MINMAXINFO structure. diff --git a/intern/ghost/intern/GHOST_Window.h b/intern/ghost/intern/GHOST_Window.h index 15b81998aab..ac31c54666b 100644 --- a/intern/ghost/intern/GHOST_Window.h +++ b/intern/ghost/intern/GHOST_Window.h @@ -295,6 +295,22 @@ public: return 1.0f; } +#ifdef WITH_INPUT_IME + virtual void beginIME(GHOST_TInt32 x, + GHOST_TInt32 y, + GHOST_TInt32 w, + GHOST_TInt32 h, + int completed) + { + /* do nothing temporarily if not in windows */ + } + + virtual void endIME() + { + /* do nothing temporarily if not in windows */ + } +#endif /* WITH_INPUT_IME */ + protected: /** * Tries to install a rendering context in this window. diff --git a/intern/ghost/intern/GHOST_WindowWin32.cpp b/intern/ghost/intern/GHOST_WindowWin32.cpp index 64ea7192616..4e384881f2c 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.cpp +++ b/intern/ghost/intern/GHOST_WindowWin32.cpp @@ -1050,3 +1050,16 @@ GHOST_TSuccess GHOST_WindowWin32::endProgressBar() return GHOST_kFailure; } + +#ifdef WITH_INPUT_IME +void GHOST_WindowWin32::beginIME(GHOST_TInt32 x, GHOST_TInt32 y, GHOST_TInt32 w, GHOST_TInt32 h, int completed) +{ + this->getImeInput()->BeginIME(this->getHWND(), GHOST_Rect(x, y - h , x, y), (bool)completed); +} + + +void GHOST_WindowWin32::endIME() +{ + this->getImeInput()->EndIME(this->getHWND()); +} +#endif /* WITH_INPUT_IME */ diff --git a/intern/ghost/intern/GHOST_WindowWin32.h b/intern/ghost/intern/GHOST_WindowWin32.h index 7b12d8c583e..c4575d0f9b0 100644 --- a/intern/ghost/intern/GHOST_WindowWin32.h +++ b/intern/ghost/intern/GHOST_WindowWin32.h @@ -39,6 +39,9 @@ #include "GHOST_Window.h" #include "GHOST_TaskbarWin32.h" +#ifdef WITH_INPUT_IME +# include "GHOST_ImeWin32.h" +#endif #include #define PACKETDATA (PK_BUTTONS | PK_NORMAL_PRESSURE | PK_ORIENTATION | PK_CURSOR) @@ -253,6 +256,17 @@ public: /** if the window currently resizing */ bool m_inLiveResize; +#ifdef WITH_INPUT_IME + GHOST_ImeWin32 *getImeInput() {return &m_imeImput;} + + virtual void beginIME( + GHOST_TInt32 x, GHOST_TInt32 y, + GHOST_TInt32 w, GHOST_TInt32 h, + int completed); + + virtual void endIME(); +#endif /* WITH_INPUT_IME */ + private: /** @@ -339,6 +353,11 @@ private: /** Hwnd to parent window */ GHOST_TEmbedderWindowID m_parentWindowHwnd; + +#ifdef WITH_INPUT_IME + /** Handle input method editors event */ + GHOST_ImeWin32 m_imeImput; +#endif }; #endif // __GHOST_WINDOWWIN32_H__ diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index ac903c7a8d2..0950edac46a 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -5677,6 +5677,9 @@ static void direct_link_windowmanager(FileData *fd, wmWindowManager *wm) win->eventstate = NULL; win->curswin = NULL; win->tweak = NULL; +#ifdef WIN32 + win->ime_data = NULL; +#endif BLI_listbase_clear(&win->queue); BLI_listbase_clear(&win->handlers); diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 42f67379dfd..2251f3fd0e4 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -313,6 +313,7 @@ void UI_draw_box_shadow(unsigned char alpha, float minx, float miny, float maxx, void UI_draw_roundbox_gl_mode(int mode, float minx, float miny, float maxx, float maxy, float rad); void UI_draw_roundbox_shade_x(int mode, float minx, float miny, float maxx, float maxy, float rad, float shadetop, float shadedown); void UI_draw_roundbox_shade_y(int mode, float minx, float miny, float maxx, float maxy, float rad, float shadeLeft, float shadeRight); +void UI_draw_text_underline(int pos_x, int pos_y, int len, int height); /* state for scrolldrawing */ #define UI_SCROLL_PRESSED (1 << 0) diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index b921d17104c..972eca747b9 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -71,6 +71,12 @@ if(WITH_PYTHON) add_definitions(-DWITH_PYTHON) endif() +if(WIN32) + if(WITH_INPUT_IME) + add_definitions(-DWITH_INPUT_IME) + endif() +endif() + add_definitions(${GL_DEFINITIONS}) blender_add_lib(bf_editor_interface "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/interface/SConscript b/source/blender/editors/interface/SConscript index 303ab7ff286..5af8bba5a9f 100644 --- a/source/blender/editors/interface/SConscript +++ b/source/blender/editors/interface/SConscript @@ -48,6 +48,10 @@ incs = [ defs = env['BF_GL_DEFINITIONS'] +if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'win64-vc', 'win64-mingw'): + if env['WITH_BF_IME']: + defs.append('WITH_INPUT_IME') + if env['WITH_BF_INTERNATIONAL']: defs.append('WITH_INTERNATIONAL') diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 92017e7a967..4cbb02c4dd5 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -209,6 +209,11 @@ void ui_window_to_region(const ARegion *ar, int *x, int *y) *y -= ar->winrct.ymin; } +void ui_region_to_window(const ARegion *ar, int *x, int *y) +{ + *x += ar->winrct.xmin; + *y += ar->winrct.ymin; +} /* ******************* block calc ************************* */ void ui_block_translate(uiBlock *block, int x, int y) diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c index 4dd952f0753..bcd9b9a5547 100644 --- a/source/blender/editors/interface/interface_draw.c +++ b/source/blender/editors/interface/interface_draw.c @@ -395,6 +395,12 @@ void UI_draw_roundbox(float minx, float miny, float maxx, float maxy, float rad) ui_draw_anti_roundbox(GL_POLYGON, minx, miny, maxx, maxy, rad, roundboxtype & UI_RB_ALPHA); } +void UI_draw_text_underline(int pos_x, int pos_y, int len, int height) +{ + int ofs_y = 4 * U.pixelsize; + glRecti(pos_x, pos_y - ofs_y, pos_x + len, pos_y - ofs_y + (height * U.pixelsize)); +} + /* ************** SPECIAL BUTTON DRAWING FUNCTIONS ************* */ void ui_draw_but_IMAGE(ARegion *UNUSED(ar), uiBut *but, uiWidgetColors *UNUSED(wcol), const rcti *rect) diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 3a312808cee..b39c786fba7 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -87,6 +87,10 @@ #include "WM_api.h" #include "WM_types.h" +#ifdef WITH_INPUT_IME +# include "wm_window.h" +#endif + /* place the mouse at the scaled down location when un-grabbing */ #define USE_CONT_MOUSE_CORRECT /* support dragging toggle buttons */ @@ -2425,8 +2429,50 @@ static bool ui_textedit_copypaste(uiBut *but, uiHandleButtonData *data, const in return changed; } +#ifdef WITH_INPUT_IME +/* enable ime, and set up uibut ime data */ +static void ui_textedit_ime_begin(wmWindow *win, uiBut *UNUSED(but)) +{ + /* XXX Is this really needed? */ + int x, y; + + /* enable IME and position to cursor, it's a trick */ + x = win->eventstate->x; + /* flip y and move down a bit, prevent the IME panel cover the edit button */ + y = win->eventstate->y - 12; + + wm_window_IME_begin(win, x, y, 0, 0, true); +} + +/* disable ime, and clear uibut ime data */ +static void ui_textedit_ime_end(wmWindow *win, uiBut *UNUSED(but)) +{ + wm_window_IME_end(win); +} + +void ui_but_ime_reposition(uiBut *but, int x, int y, bool complete) +{ + BLI_assert(but->active); + + ui_region_to_window(but->active->region, &x, &y); + wm_window_IME_begin(but->active->window, x, y - 4, 0, 0, complete); +} + +/* should be ui_but_ime_data_get */ +wmIMEData *ui_but_get_ime_data(uiBut *but) +{ + if (but->active && but->active->window) { + return but->active->window->ime_data; + } + else { + return NULL; + } +} +#endif /* WITH_INPUT_IME */ + static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data) { + wmWindow *win = CTX_wm_window(C); int len; if (data->str) { @@ -2482,12 +2528,18 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data) but->flag &= ~UI_BUT_REDALERT; ui_but_update(but); - - WM_cursor_modal_set(CTX_wm_window(C), BC_TEXTEDITCURSOR); + + WM_cursor_modal_set(win, BC_TEXTEDITCURSOR); + +#ifdef WITH_INPUT_IME + ui_textedit_ime_begin(win, but); +#endif } static void ui_textedit_end(bContext *C, uiBut *but, uiHandleButtonData *data) { + wmWindow *win = CTX_wm_window(C); + if (but) { if (ui_but_is_utf8(but)) { int strip = BLI_utf8_invalid_strip(but->editstr, strlen(but->editstr)); @@ -2518,7 +2570,11 @@ static void ui_textedit_end(bContext *C, uiBut *but, uiHandleButtonData *data) but->pos = -1; } - WM_cursor_modal_restore(CTX_wm_window(C)); + WM_cursor_modal_restore(win); + +#ifdef WITH_INPUT_IME + ui_textedit_ime_end(win, but); +#endif } static void ui_textedit_next_but(uiBlock *block, uiBut *actbut, uiHandleButtonData *data) @@ -2583,6 +2639,14 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle int retval = WM_UI_HANDLER_CONTINUE; bool changed = false, inbox = false, update = false; +#ifdef WITH_INPUT_IME + wmWindow *win = CTX_wm_window(C); + wmIMEData *ime_data = win->ime_data; + bool is_ime_composing = ime_data && ime_data->is_ime_composing; +#else + bool is_ime_composing = false; +#endif + switch (event->type) { case MOUSEMOVE: case MOUSEPAN: @@ -2603,6 +2667,12 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle case RIGHTMOUSE: case ESCKEY: if (event->val == KM_PRESS) { +#ifdef WITH_INPUT_IME + /* skips button handling since it is not wanted */ + if (is_ime_composing) { + break; + } +#endif data->cancel = true; data->escapecancel = true; button_activate_state(C, but, BUTTON_STATE_EXIT); @@ -2660,7 +2730,7 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle } } - if (event->val == KM_PRESS) { + if (event->val == KM_PRESS && !is_ime_composing) { switch (event->type) { case VKEY: case XKEY: @@ -2776,7 +2846,15 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle break; } - if ((event->ascii || event->utf8_buf[0]) && (retval == WM_UI_HANDLER_CONTINUE)) { + if ((event->ascii || event->utf8_buf[0]) && + (retval == WM_UI_HANDLER_CONTINUE) +#ifdef WITH_INPUT_IME + && + !is_ime_composing && + !WM_event_is_ime_switch(event) +#endif + ) + { char ascii = event->ascii; const char *utf8_buf = event->utf8_buf; @@ -2811,6 +2889,25 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle update = true; } +#ifdef WITH_INPUT_IME + if (event->type == WM_IME_COMPOSITE_START || event->type == WM_IME_COMPOSITE_EVENT) { + changed = true; + + if (event->type == WM_IME_COMPOSITE_START && but->selend > but->selsta) { + ui_textedit_delete_selection(but, data); + } + if (event->type == WM_IME_COMPOSITE_EVENT && ime_data->result_len) { + ui_textedit_type_buf( + but, data, + ime_data->str_result, + ime_data->result_len); + } + } + else if (event->type == WM_IME_COMPOSITE_END) { + changed = true; + } +#endif + if (changed) { /* only update when typing for TAB key */ if (update && data->interactive) { diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 328822e47ef..2c3a239b2a2 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -433,6 +433,7 @@ extern void ui_block_to_window_rctf(const struct ARegion *ar, uiBlock *block, rc extern void ui_window_to_block_fl(const struct ARegion *ar, uiBlock *block, float *x, float *y); extern void ui_window_to_block(const struct ARegion *ar, uiBlock *block, int *x, int *y); extern void ui_window_to_region(const ARegion *ar, int *x, int *y); +extern void ui_region_to_window(const struct ARegion *ar, int *x, int *y); extern double ui_but_value_get(uiBut *but); extern void ui_but_value_set(uiBut *but, double value); @@ -627,6 +628,11 @@ void ui_panel_menu(struct bContext *C, ARegion *ar, Panel *pa); uiBut *ui_but_find_old(uiBlock *block_old, const uiBut *but_new); uiBut *ui_but_find_new(uiBlock *block_old, const uiBut *but_new); +#ifdef WITH_INPUT_IME +void ui_but_ime_reposition(uiBut *but, int x, int y, bool complete); +struct wmIMEData *ui_but_get_ime_data(uiBut *but); +#endif + /* interface_widgets.c */ void ui_draw_anti_tria(float x1, float y1, float x2, float y2, float x3, float y3); void ui_draw_anti_roundbox(int mode, float minx, float miny, float maxx, float maxy, float rad, bool use_alpha); diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index c9def075666..d6c51b6b220 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -59,6 +59,10 @@ #include "interface_intern.h" +#ifdef WITH_INPUT_IME +# include "WM_types.h" +#endif + /* icons are 80% of height of button (16 pixels inside 20 height) */ #define ICON_SIZE_FROM_BUTRECT(rect) (0.8f * BLI_rcti_size_y(rect)) @@ -1232,6 +1236,50 @@ static void ui_text_clip_right_label(uiFontStyle *fstyle, uiBut *but, const rcti BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT); } +#ifdef WITH_INPUT_IME +static void widget_draw_text_ime_underline( + uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *but, const rcti *rect, + const wmIMEData *ime_data, const char *drawstr) +{ + int ofs_x, width; + int rect_x = BLI_rcti_size_x(rect); + int sel_start = ime_data->sel_start, sel_end = ime_data->sel_end; + + if (drawstr[0] != 0) { + if (but->pos >= but->ofs) { + ofs_x = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but->pos - but->ofs); + } + else { + ofs_x = 0; + } + + width = BLF_width(fstyle->uifont_id, drawstr + but->ofs, + ime_data->composite_len + but->pos - but->ofs); + + glColor4ubv((unsigned char *)wcol->text); + UI_draw_text_underline(rect->xmin + ofs_x, rect->ymin + 6 * U.pixelsize, min_ii(width, rect_x - 2) - ofs_x, 1); + + /* draw the thick line */ + if (sel_start != -1 && sel_end != -1) { + sel_end -= sel_start; + sel_start += but->pos; + + if (sel_start >= but->ofs) { + ofs_x = BLF_width(fstyle->uifont_id, drawstr + but->ofs, sel_start - but->ofs); + } + else { + ofs_x = 0; + } + + width = BLF_width(fstyle->uifont_id, drawstr + but->ofs, + sel_end + sel_start - but->ofs); + + UI_draw_text_underline(rect->xmin + ofs_x, rect->ymin + 6 * U.pixelsize, min_ii(width, rect_x - 2) - ofs_x, 2); + } + } +} +#endif /* WITH_INPUT_IME */ + static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *but, rcti *rect) { int drawstr_left_len = UI_MAX_DRAW_STR; @@ -1239,6 +1287,10 @@ static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *b const char *drawstr_right = NULL; bool use_right_only = false; +#ifdef WITH_INPUT_IME + const wmIMEData *ime_data; +#endif + UI_fontstyle_set(fstyle); if (but->editstr || (but->drawflag & UI_BUT_TEXT_LEFT)) @@ -1266,13 +1318,30 @@ static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *b /* max length isn't used in this case, * we rely on string being NULL terminated. */ drawstr_left_len = INT_MAX; - drawstr = but->editstr; + +#ifdef WITH_INPUT_IME + /* FIXME, IME is modifying 'const char *drawstr! */ + ime_data = ui_but_get_ime_data(but); + + if (ime_data && ime_data->composite_len) { + /* insert composite string into cursor pos */ + BLI_snprintf((char *)drawstr, UI_MAX_DRAW_STR, "%s%s%s", + but->editstr, ime_data->str_composite, + but->editstr + but->pos); + } + else +#endif + { + drawstr = but->editstr; + } } } - /* text button selection and cursor */ + /* text button selection, cursor, composite underline */ if (but->editstr && but->pos != -1) { + int but_pos_ofs; + int tx, ty; /* text button selection */ if ((but->selend - but->selsta) > 0) { @@ -1298,18 +1367,44 @@ static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *b } /* text cursor */ + but_pos_ofs = but->pos; + +#ifdef WITH_INPUT_IME + /* if is ime compositing, move the cursor */ + if (ime_data && ime_data->composite_len && ime_data->cursor_pos != -1) { + but_pos_ofs += ime_data->cursor_pos; + } +#endif + if (but->pos >= but->ofs) { int t; if (drawstr[0] != 0) { - t = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but->pos - but->ofs); + t = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but_pos_ofs - but->ofs); } else { t = 0; } glColor3f(0.20, 0.6, 0.9); - glRecti(rect->xmin + t, rect->ymin + 2, rect->xmin + t + 2, rect->ymax - 2); + + tx = rect->xmin + t + 2; + ty = rect->ymin + 2; + + /* draw cursor */ + glRecti(rect->xmin + t, ty, tx, rect->ymax - 2); } + +#ifdef WITH_INPUT_IME + if (ime_data && ime_data->composite_len) { + /* ime cursor following */ + if (but->pos >= but->ofs) { + ui_but_ime_reposition(but, tx + 5, ty + 3, false); + } + + /* composite underline */ + widget_draw_text_ime_underline(fstyle, wcol, but, rect, ime_data, drawstr); + } +#endif } if (fstyle->kerning == 1) diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h index 73a70b48712..1a0562ae8c5 100644 --- a/source/blender/makesdna/DNA_windowmanager_types.h +++ b/source/blender/makesdna/DNA_windowmanager_types.h @@ -165,6 +165,13 @@ enum { WM_INIT_KEYMAP = (1<<1), }; +/* IME is win32 only! */ +#ifndef WIN32 +# ifdef __GNUC__ +# define ime_data ime_data __attribute__ ((deprecated)) +# endif +#endif + /* the savable part, rest of data is local in ghostwinlay */ typedef struct wmWindow { struct wmWindow *next, *prev; @@ -197,6 +204,10 @@ typedef struct wmWindow { struct wmGesture *tweak; /* internal for wm_operators.c */ + /* Input Method Editor data - complex character input (esp. for asian character input) + * Currently WIN32, runtime-only data */ + struct wmIMEData *ime_data; + int drawmethod, drawfail; /* internal for wm_draw.c only */ void *drawdata; /* internal for wm_draw.c only */ @@ -208,6 +219,10 @@ typedef struct wmWindow { ListBase gesture; /* gesture stuff */ } wmWindow; +#ifdef ime_data +# undef ime_data +#endif + /* These two Lines with # tell makesdna this struct can be excluded. */ /* should be something like DNA_EXCLUDE * but the preprocessor first removes all comments, spaces etc */ diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index 4cbadda1732..78b5d499644 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -130,6 +130,12 @@ if(WITH_BUILDINFO) add_definitions(-DWITH_BUILDINFO) endif() +if(WIN32) + if(WITH_INPUT_IME) + add_definitions(-DWITH_INPUT_IME) + endif() +endif() + if(WITH_COMPOSITOR) add_definitions(-DWITH_COMPOSITOR) endif() diff --git a/source/blender/windowmanager/SConscript b/source/blender/windowmanager/SConscript index 3c44ff6107c..a6f64f7cdae 100644 --- a/source/blender/windowmanager/SConscript +++ b/source/blender/windowmanager/SConscript @@ -69,6 +69,10 @@ if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc', ' if env['BF_BUILDINFO']: defs.append('WITH_BUILDINFO') +if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'win64-vc', 'win64-mingw'): + if env['WITH_BF_IME']: + defs.append('WITH_INPUT_IME') + if env['WITH_BF_INTERNATIONAL']: defs.append('WITH_INTERNATIONAL') diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index ced7222b046..d2abfd419d1 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -462,6 +462,10 @@ void WM_event_ndof_to_quat(const struct wmNDOFMotionData *ndof, float q[4 float WM_event_tablet_data(const struct wmEvent *event, int *pen_flip, float tilt[2]); bool WM_event_is_tablet(const struct wmEvent *event); +#ifdef WITH_INPUT_IME +bool WM_event_is_ime_switch(const struct wmEvent *event); +#endif + #ifdef __cplusplus } #endif diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 4b6b5e95366..d9924cfb053 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -570,6 +570,24 @@ typedef struct wmOperatorType { } wmOperatorType; +#ifdef WITH_INPUT_IME +/* *********** Input Method Editor (IME) *********** */ + +/* similar to GHOST_TEventImeData */ +typedef struct wmIMEData { + size_t result_len, composite_len; + + char *str_result; /* utf8 encoding */ + char *str_composite; /* utf8 encoding */ + + int cursor_pos; /* cursor position in the IME composition. */ + int sel_start; /* beginning of the selection */ + int sel_end; /* end of the selection */ + + bool is_ime_composing; +} wmIMEData; +#endif + /* **************** Paint Cursor ******************* */ typedef void (*wmPaintCursorDraw)(struct bContext *C, int, int, void *customdata); diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index cca267a9fbb..3de34e7ca3f 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -3373,6 +3373,33 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U break; } +#ifdef WITH_INPUT_IME + case GHOST_kEventImeCompositionStart: + { + event.val = KM_PRESS; + win->ime_data = customdata; + win->ime_data->is_ime_composing = true; + event.type = WM_IME_COMPOSITE_START; + wm_event_add(win, &event); + break; + } + case GHOST_kEventImeComposition: + { + event.val = KM_PRESS; + event.type = WM_IME_COMPOSITE_EVENT; + wm_event_add(win, &event); + break; + } + case GHOST_kEventImeCompositionEnd: + { + event.val = KM_PRESS; + win->ime_data->is_ime_composing = false; + event.type = WM_IME_COMPOSITE_END; + wm_event_add(win, &event); + break; + } +#endif /* WITH_INPUT_IME */ + } #if 0 @@ -3479,5 +3506,12 @@ bool WM_event_is_tablet(const struct wmEvent *event) return (event->tablet_data) ? true : false; } +#ifdef WITH_INPUT_IME +/* most os using ctrl/oskey + space to switch ime, avoid added space */ +bool WM_event_is_ime_switch(const struct wmEvent *event) { + return event->val == KM_PRESS && event->type == SPACEKEY && + (event->ctrl || event->oskey || event->shift || event->alt); +} +#endif /** \} */ diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 46a20d3bf88..0b33dbb2c32 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -1524,3 +1524,20 @@ bool WM_window_is_fullscreen(wmWindow *win) return win->windowstate == GHOST_kWindowStateFullScreen; } + +#ifdef WITH_INPUT_IME +void wm_window_IME_begin(wmWindow *win, int x, int y, int w, int h, bool complete) +{ + BLI_assert(win && (win->ime_data == NULL)); + + GHOST_BeginIME(win->ghostwin, x, win->sizey - y, w, h, complete); +} + +void wm_window_IME_end(wmWindow *win) +{ + BLI_assert(win && win->ime_data); + + GHOST_EndIME(win->ghostwin); + win->ime_data = NULL; +} +#endif /* WITH_INPUT_IME */ diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h index fe6d3431183..2301405a8ea 100644 --- a/source/blender/windowmanager/wm_event_types.h +++ b/source/blender/windowmanager/wm_event_types.h @@ -86,6 +86,13 @@ enum { * paint and drawing tools however will want to handle these. */ INBETWEEN_MOUSEMOVE = 0x0011, +/* IME event, GHOST_kEventImeCompositionStart in ghost */ + WM_IME_COMPOSITE_START = 0x0014, +/* IME event, GHOST_kEventImeComposition in ghost */ + WM_IME_COMPOSITE_EVENT = 0x0015, +/* IME event, GHOST_kEventImeCompositionEnd in ghost */ + WM_IME_COMPOSITE_END = 0x0016, + /* *** Start of keyboard codes. *** */ /* standard keyboard. diff --git a/source/blender/windowmanager/wm_window.h b/source/blender/windowmanager/wm_window.h index 9c9c79d2f54..833234b0f13 100644 --- a/source/blender/windowmanager/wm_window.h +++ b/source/blender/windowmanager/wm_window.h @@ -69,6 +69,11 @@ wmWindow *wm_window_copy (bContext *C, wmWindow *winorig); void wm_window_testbreak (void); +#ifdef WITH_INPUT_IME +void wm_window_IME_begin (wmWindow *win, int x, int y, int w, int h, bool complete); +void wm_window_IME_end (wmWindow *win); +#endif + /* *************** window operators ************** */ int wm_window_duplicate_exec(bContext *C, struct wmOperator *op); int wm_window_fullscreen_toggle_exec(bContext *C, struct wmOperator *op);