diff options
Diffstat (limited to 'indra/llwindow')
-rw-r--r-- | indra/llwindow/llwindow.cpp | 50 | ||||
-rw-r--r-- | indra/llwindow/llwindow.h | 13 | ||||
-rw-r--r-- | indra/llwindow/llwindowmacosx.cpp | 75 | ||||
-rw-r--r-- | indra/llwindow/llwindowmacosx.h | 8 | ||||
-rw-r--r-- | indra/llwindow/llwindowsdl.cpp | 7 | ||||
-rw-r--r-- | indra/llwindow/llwindowwin32.cpp | 172 | ||||
-rw-r--r-- | indra/llwindow/llwindowwin32.h | 11 |
7 files changed, 325 insertions, 11 deletions
diff --git a/indra/llwindow/llwindow.cpp b/indra/llwindow/llwindow.cpp index 650398cb65..3cacc7df0f 100644 --- a/indra/llwindow/llwindow.cpp +++ b/indra/llwindow/llwindow.cpp @@ -221,7 +221,8 @@ LLWindow::LLWindow(BOOL fullscreen, U32 flags) mIsMouseClipping(FALSE), mSwapMethod(SWAP_METHOD_UNDEFINED), mHideCursorPermanent(FALSE), - mFlags(flags) + mFlags(flags), + mHighSurrogate(0) { for (U32 i = 0; i < 6; i++) { @@ -276,6 +277,53 @@ void LLWindow::setCallbacks(LLWindowCallbacks *callbacks) } } +#define UTF16_IS_HIGH_SURROGATE(U) ((U16)((U) - 0xD800) < 0x0400) +#define UTF16_IS_LOW_SURROGATE(U) ((U16)((U) - 0xDC00) < 0x0400) +#define UTF16_SURROGATE_PAIR_TO_UTF32(H,L) (((H) << 10) + (L) - (0xD800 << 10) - 0xDC00 + 0x00010000) + +void LLWindow::handleUnicodeUTF16(U16 utf16, MASK mask) +{ + // Note that we could discard unpaired surrogates, but I'm + // following the Unicode Consortium's recommendation here; + // that is, to preserve those unpaired surrogates in UTF-32 + // values. _To_preserve_ means to pass to the callback in our + // context. + + if (mHighSurrogate == 0) + { + if (UTF16_IS_HIGH_SURROGATE(utf16)) + { + mHighSurrogate = utf16; + } + else + { + mCallbacks->handleUnicodeChar(utf16, mask); + } + } + else + { + if (UTF16_IS_LOW_SURROGATE(utf16)) + { + /* A legal surrogate pair. */ + mCallbacks->handleUnicodeChar(UTF16_SURROGATE_PAIR_TO_UTF32(mHighSurrogate, utf16), mask); + mHighSurrogate = 0; + } + else if (UTF16_IS_HIGH_SURROGATE(utf16)) + { + /* Two consecutive high surrogates. */ + mCallbacks->handleUnicodeChar(mHighSurrogate, mask); + mHighSurrogate = utf16; + } + else + { + /* A non-low-surrogate preceeded by a high surrogate. */ + mCallbacks->handleUnicodeChar(mHighSurrogate, mask); + mHighSurrogate = 0; + mCallbacks->handleUnicodeChar(utf16, mask); + } + } +} + // // LLSplashScreen // diff --git a/indra/llwindow/llwindow.h b/indra/llwindow/llwindow.h index 6e26285e29..1347d8e94c 100644 --- a/indra/llwindow/llwindow.h +++ b/indra/llwindow/llwindow.h @@ -198,6 +198,10 @@ public: // return a platform-specific window reference (HWND on Windows, WindowRef on the Mac) virtual void *getPlatformWindow() = 0; + // control platform's Language Text Input mechanisms. + virtual void allowLanguageTextInput( BOOL b ) {}; + virtual void setLanguageTextInput( LLCoordWindow pos ) {}; + protected: LLWindow(BOOL fullscreen, U32 flags); virtual ~LLWindow() {} @@ -226,6 +230,15 @@ protected: U32 mFlags; F32 mJoyAxis[6]; U8 mJoyButtonState[16]; + U16 mHighSurrogate; + + // Handle a UTF-16 encoding unit received from keyboard. + // Converting the series of UTF-16 encoding units to UTF-32 data, + // this method passes the resulting UTF-32 data to mCallback's + // handleUnicodeChar. The mask should be that to be passed to the + // callback. This method uses mHighSurrogate as a dedicated work + // variable. + void handleUnicodeUTF16(U16 utf16, MASK mask); friend class LLWindowManager; }; diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp index 62743d4d08..24c9149cb4 100644 --- a/indra/llwindow/llwindowmacosx.cpp +++ b/indra/llwindow/llwindowmacosx.cpp @@ -219,6 +219,10 @@ LLWindowMacOSX::LLWindowMacOSX(char *title, char *name, S32 x, S32 y, S32 width, mNeedsResize = FALSE; mOverrideAspectRatio = 0.f; mMinimized = FALSE; + mTSMDocument = NULL; // Just in case. + mLanguageTextInputAllowed = FALSE; + mTSMScriptCode = 0; + mTSMLangCode = 0; // For reasons that aren't clear to me, LLTimers seem to be created in the "started" state. // Since the started state of this one is used to track whether the NMRec has been installed, it wants to start out in the "stopped" state. @@ -459,6 +463,29 @@ BOOL LLWindowMacOSX::createContext(int x, int y, int width, int height, int bits } + { + // Create and initialize our TSM document for language text input. + // If an error occured, we can do nothing better than simply ignore it. + // mTSMDocument will be kept NULL in case. + if (mTSMDocument) + { + DeactivateTSMDocument(mTSMDocument); + DeleteTSMDocument(mTSMDocument); + mTSMDocument = NULL; + } + static InterfaceTypeList types = { kUnicodeDocument }; + OSErr err = NewTSMDocument(1, types, &mTSMDocument, 0); + if (err != noErr) + { + llwarns << "createContext: couldn't create a TSMDocument (" << err << ")" << llendl; + } + if (mTSMDocument) + { + UseInputWindow(mTSMDocument, TRUE); + ActivateTSMDocument(mTSMDocument); + } + } + if(mContext == NULL) { AGLRendererInfo rendererInfo = NULL; @@ -904,6 +931,15 @@ void LLWindowMacOSX::destroyContext() mWindowHandlerRef = NULL; } + // Cleanup any TSM document we created. + if(mTSMDocument != NULL) + { + llinfos << "destroyContext: deleting TSM document" << llendl; + DeactivateTSMDocument(mTSMDocument); + DeleteTSMDocument(mTSMDocument); + mTSMDocument = NULL; + } + // Close the window if(mWindow != NULL) { @@ -1509,7 +1545,7 @@ void LLWindowMacOSX::flashIcon(F32 seconds) OSErr err; mBounceTime = seconds; - memset(&mBounceRec, sizeof(mBounceRec), 0); + memset(&mBounceRec, 0, sizeof(mBounceRec)); mBounceRec.qType = nmType; mBounceRec.nmMark = 1; err = NMInstall(&mBounceRec); @@ -2221,6 +2257,10 @@ OSStatus LLWindowMacOSX::eventHandler (EventHandlerCallRef myHandler, EventRef e switch(evtKind) { case kEventWindowActivated: + if (mTSMDocument) + { + ActivateTSMDocument(mTSMDocument); + } mCallbacks->handleFocus(this); break; case kEventWindowDeactivated: @@ -2932,4 +2972,37 @@ static long getDictLong (CFDictionaryRef refDict, CFStringRef key) return int_value; // otherwise return the long value } +void LLWindowMacOSX::allowLanguageTextInput(BOOL b) +{ + ScriptLanguageRecord script_language; + + if (b == mLanguageTextInputAllowed) + { + return; + } + mLanguageTextInputAllowed = b; + + if (b) + { + if (mTSMScriptCode != smRoman) + { + script_language.fScript = mTSMScriptCode; + script_language.fLanguage = mTSMLangCode; + SetTextServiceLanguage(&script_language); + } + } + else + { + GetTextServiceLanguage(&script_language); + mTSMScriptCode = script_language.fScript; + mTSMLangCode = script_language.fLanguage; + if (mTSMScriptCode != smRoman) + { + script_language.fScript = smRoman; + script_language.fLanguage = langEnglish; + SetTextServiceLanguage(&script_language); + } + } +} + #endif // LL_DARWIN diff --git a/indra/llwindow/llwindowmacosx.h b/indra/llwindow/llwindowmacosx.h index 1927f9bf31..fd9e5c8484 100644 --- a/indra/llwindow/llwindowmacosx.h +++ b/indra/llwindow/llwindowmacosx.h @@ -88,6 +88,8 @@ public: /*virtual*/ void *getPlatformWindow(); /*virtual*/ void bringToFront() {}; + /*virtual*/ void allowLanguageTextInput(BOOL b); + protected: LLWindowMacOSX( char *title, char *name, int x, int y, int width, int height, U32 flags, @@ -163,6 +165,12 @@ protected: NMRec mBounceRec; LLTimer mBounceTimer; + // Imput method management through Text Service Manager. + TSMDocumentID mTSMDocument; + BOOL mLanguageTextInputAllowed; + ScriptCode mTSMScriptCode; + LangCode mTSMLangCode; + friend class LLWindowManager; }; diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp index 6c1ee967df..3ccf0d1250 100644 --- a/indra/llwindow/llwindowsdl.cpp +++ b/indra/llwindow/llwindowsdl.cpp @@ -22,7 +22,9 @@ #include "indra_constants.h" #if LL_GTK +extern "C" { # include "gtk/gtk.h" +} #endif // LL_GTK #if LL_LINUX @@ -1964,7 +1966,10 @@ void LLWindowSDL::gatherInput() SDLReallyCaptureInput(TRUE); if (event.key.keysym.unicode) - mCallbacks->handleUnicodeChar(event.key.keysym.unicode, gKeyboard->currentMask(FALSE)); + { + handleUnicodeUTF16(event.key.keysym.unicode, + gKeyboard->currentMask(FALSE)); + } break; case SDL_KEYUP: diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 095b2113a7..afa78984ea 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -17,6 +17,7 @@ #include <mapi.h> #include <process.h> // for _spawn #include <shellapi.h> +#include <Imm.h> // Require DirectInput version 8 #define DIRECTINPUT_VERSION 0x0800 @@ -64,6 +65,98 @@ void show_window_creation_error(const char* title) //static BOOL LLWindowWin32::sIsClassRegistered = FALSE; +BOOL LLWindowWin32::sLanguageTextInputAllowed = TRUE; /* XXX */ +BOOL LLWindowWin32::sWinIMEOpened = FALSE; +HKL LLWindowWin32::sWinInputLocale; +DWORD LLWindowWin32::sWinIMEConversionMode; +DWORD LLWindowWin32::sWinIMESentenceMode; + +// The following class LLWinImm delegates Windows IMM APIs. +// We need this because some language versions of Windows, +// e.g., US version of Windows XP, doesn't install IMM32.DLL +// as a default, and we can't link against imm32.lib statically. +// I believe DLL loading of this type is best suited to do +// in a static initialization of a class. What I'm not sure is +// whether it follows the Linden Conding Standard... +// See http://wiki.secondlife.com/wiki/Coding_standards#Static_Members + +class LLWinImm +{ +public: + static bool isAvailable() { return sTheInstance.mHImmDll != NULL; } + +public: + // Wrappers for IMM API. + static BOOL isIME(HKL hkl) { return sTheInstance.mImmIsIME(hkl); } + static HIMC getContext(HWND hwnd) { return sTheInstance.mImmGetContext(hwnd); } + static BOOL releaseContext(HWND hwnd, HIMC himc) { return sTheInstance.mImmReleaseContext(hwnd, himc); } + static BOOL getOpenStatus(HIMC himc) { return sTheInstance.mImmGetOpenStatus(himc); } + static BOOL setOpenStatus(HIMC himc, BOOL status) { return sTheInstance.mImmSetOpenStatus(himc, status); } + static BOOL getConversionStatus(HIMC himc, LPDWORD conversion, LPDWORD sentence) { return sTheInstance.mImmGetConversionStatus(himc, conversion, sentence); } + static BOOL setConversionStatus(HIMC himc, DWORD conversion, DWORD sentence) { return sTheInstance.mImmSetConversionStatus(himc, conversion, sentence); } + +private: + LLWinImm(); + ~LLWinImm(); + +private: + // Pointers to IMM API. + BOOL (WINAPI *mImmIsIME)(HKL); + HIMC (WINAPI *mImmGetContext)(HWND); + BOOL (WINAPI *mImmReleaseContext)(HWND, HIMC); + BOOL (WINAPI *mImmGetOpenStatus)(HIMC); + BOOL (WINAPI *mImmSetOpenStatus)(HIMC, BOOL); + BOOL (WINAPI *mImmGetConversionStatus)(HIMC, LPDWORD, LPDWORD); + BOOL (WINAPI *mImmSetConversionStatus)(HIMC, DWORD, DWORD); + +private: + HMODULE mHImmDll; + static LLWinImm sTheInstance; +}; + +LLWinImm LLWinImm::sTheInstance; + +LLWinImm::LLWinImm() +{ + mHImmDll = LoadLibraryA("Imm32"); + if (mHImmDll != NULL) + { + mImmIsIME = (BOOL (WINAPI *)(HKL)) GetProcAddress(mHImmDll, "ImmIsIME"); + mImmGetContext = (HIMC (WINAPI *)(HWND)) GetProcAddress(mHImmDll, "ImmGetContext"); + mImmReleaseContext = (BOOL (WINAPI *)(HWND, HIMC)) GetProcAddress(mHImmDll, "ImmReleaseContext"); + mImmGetOpenStatus = (BOOL (WINAPI *)(HIMC)) GetProcAddress(mHImmDll, "ImmGetOpenStatus"); + mImmSetOpenStatus = (BOOL (WINAPI *)(HIMC, BOOL)) GetProcAddress(mHImmDll, "ImmSetOpenStatus"); + mImmGetConversionStatus = (BOOL (WINAPI *)(HIMC, LPDWORD, LPDWORD)) GetProcAddress(mHImmDll, "ImmGetConversionStatus"); + mImmSetConversionStatus = (BOOL (WINAPI *)(HIMC, DWORD, DWORD)) GetProcAddress(mHImmDll, "ImmSetConversionStatus"); + if (mImmIsIME == NULL || + mImmGetContext == NULL || + mImmReleaseContext == NULL || + mImmGetOpenStatus == NULL || + mImmSetOpenStatus == NULL || + mImmGetConversionStatus == NULL || + mImmSetConversionStatus == NULL) + { + // If any of the above API entires are not found, we can't use IMM API. + // So, turn off the IMM support. We should log some warning message in + // the case, since it is very unusual; these APIs are available from + // the beginning, and all versions of IMM32.DLL should have them all. + // Unfortunately, this code may be executed before initialization of + // the logging channel (llwarns), and we can't do it here... Yes, this + // is one of disadvantages to use static constraction to DLL loading. + FreeLibrary(mHImmDll); + mHImmDll = NULL; + } + } +} + +LLWinImm::~LLWinImm() +{ + if (mHImmDll != NULL) + { + FreeLibrary(mHImmDll); + mHImmDll = NULL; + } +} LPDIRECTINPUT8 g_pDI = NULL; @@ -91,6 +184,10 @@ LLWindowWin32::LLWindowWin32(char *title, char *name, S32 x, S32 y, S32 width, // Initialize the keyboard gKeyboard = new LLKeyboardWin32(); + // Initialize (boot strap) the Language text input management, + // based on the system's (user's) default settings. + allowLanguageTextInput(FALSE); + GLuint pixel_format; WNDCLASS wc; DWORD dw_ex_style; @@ -327,7 +424,6 @@ LLWindowWin32::LLWindowWin32(char *title, char *name, S32 x, S32 y, S32 width, if (!mWindowHandle) { - DestroyWindow(mWindowHandle); OSMessageBox("Window creation error", "Error", OSMB_OK); return; } @@ -1906,6 +2002,15 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ case WM_CHAR: // Should really use WM_UNICHAR eventually, but it requires a specific Windows version and I need // to figure out how that works. - Doug + // + // ... Well, I don't think so. + // How it works is explained in Win32 API document, but WM_UNICHAR didn't work + // as specified at least on Windows XP SP1 Japanese version. I have never used + // it since then, and I'm not sure whether it has been fixed now, but I don't think + // it is worth trying. The good old WM_CHAR works just fine even for supplementary + // characters. We just need to take care of surrogate pairs sent as two WM_CHAR's + // by ourselves. It is not that tough. -- Alissa Sabre @ SL + // // llinfos << "WM_CHAR: " << w_param << llendl; if (gDebugWindowProc) { @@ -1913,11 +2018,10 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ << " key " << S32(w_param) << llendl; } - if (window_imp->mCallbacks->handleUnicodeChar(w_param, gKeyboard->currentMask(FALSE))) - { - return 0; - } - break; + // Even if LLWindowCallbacks::handleUnicodeChar(llwchar, BOOL) returned FALSE, + // we *did* processed the event, so I believe we should not pass it to DefWindowProc... + window_imp->handleUnicodeUTF16((U16)w_param, gKeyboard->currentMask(FALSE)); + return 0; case WM_LBUTTONDOWN: { @@ -2951,7 +3055,7 @@ void spawn_web_browser(const char* escaped_url ) char reg_path_str[256]; /* Flawfinder: ignore */ snprintf(reg_path_str, sizeof(reg_path_str), "%s\\shell\\open\\command", gURLProtocolWhitelistHandler[i]); /* Flawfinder: ignore */ WCHAR reg_path_wstr[256]; - mbstowcs(reg_path_wstr, reg_path_str, 1024); + mbstowcs(reg_path_wstr, reg_path_str, sizeof(reg_path_wstr)/sizeof(reg_path_wstr[0])); HKEY key; WCHAR browser_open_wstr[1024]; @@ -3082,6 +3186,58 @@ void LLWindowWin32::bringToFront() void LLWindowWin32::focusClient() { SetFocus ( mWindowHandle ); -}; +} + +void LLWindowWin32::allowLanguageTextInput(BOOL b) +{ + if (b == sLanguageTextInputAllowed || !LLWinImm::isAvailable()) + { + /* Not actually allowing/disallowing. Do nothing. */ + return; + } + sLanguageTextInputAllowed = b; + + if (b) + { + /* Allowing: Restore the previous IME status, + so that the user has a feeling that the previous + text input continues naturally. Be careful, however, + the IME status is meaningful only during the user keeps + using same Input Locale (aka Keyboard Layout). */ + if (sWinIMEOpened && GetKeyboardLayout(0) == sWinInputLocale) + { + HIMC himc = LLWinImm::getContext(mWindowHandle); + LLWinImm::setOpenStatus(himc, TRUE); + LLWinImm::setConversionStatus(himc, sWinIMEConversionMode, sWinIMESentenceMode); + LLWinImm::releaseContext(mWindowHandle, himc); + } + } + else + { + /* Disallowing: Turn off the IME so that succeeding + key events bypass IME and come to us directly. + However, do it after saving the current IME + status. We need to restore the status when + allowing language text input again. */ + sWinInputLocale = GetKeyboardLayout(0); + sWinIMEOpened = LLWinImm::isIME(sWinInputLocale); + if (sWinIMEOpened) + { + HIMC himc = LLWinImm::getContext(mWindowHandle); + sWinIMEOpened = LLWinImm::getOpenStatus(himc); + if (sWinIMEOpened) + { + LLWinImm::getConversionStatus(himc, &sWinIMEConversionMode, &sWinIMESentenceMode); + + /* We need both ImmSetConversionStatus and ImmSetOpenStatus here + to surely disable IME's keyboard hooking, because Some IME reacts + only on the former and some other on the latter... */ + LLWinImm::setConversionStatus(himc, IME_CMODE_NOCONVERSION, sWinIMESentenceMode); + LLWinImm::setOpenStatus(himc, FALSE); + } + LLWinImm::releaseContext(mWindowHandle, himc); + } + } +} #endif // LL_WINDOWS diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h index 487c1f09e7..5eb2cfbb79 100644 --- a/indra/llwindow/llwindowwin32.h +++ b/indra/llwindow/llwindowwin32.h @@ -86,6 +86,8 @@ public: /*virtual*/ void bringToFront(); /*virtual*/ void focusClient(); + /*virtual*/ void allowLanguageTextInput(BOOL b); + protected: LLWindowWin32( char *title, char *name, int x, int y, int width, int height, U32 flags, @@ -154,6 +156,15 @@ protected: BOOL mMousePositionModified; BOOL mInputProcessingPaused; + // The following five variables are for Language Text Input control. + // They are all static, since one context is shared by all LLWindowWin32 + // instances. + static BOOL sLanguageTextInputAllowed; + static BOOL sWinIMEOpened; + static HKL sWinInputLocale; + static DWORD sWinIMEConversionMode; + static DWORD sWinIMESentenceMode; + friend class LLWindowManager; }; |