summaryrefslogtreecommitdiff
path: root/indra/llwindow
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llwindow')
-rw-r--r--indra/llwindow/llwindow.cpp50
-rw-r--r--indra/llwindow/llwindow.h13
-rw-r--r--indra/llwindow/llwindowmacosx.cpp75
-rw-r--r--indra/llwindow/llwindowmacosx.h8
-rw-r--r--indra/llwindow/llwindowsdl.cpp7
-rw-r--r--indra/llwindow/llwindowwin32.cpp172
-rw-r--r--indra/llwindow/llwindowwin32.h11
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;
};